diff --git a/README.md b/README.md
index e4cf0f6a..acdfbfa9 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ A change has been submitted to StackForge to switch to bare-trusty for this buil
* [monasca-common-streaming](https://github.com/stackforge/monasca-common/tree/master/java/monasca-common-streaming) - Streaming related utilities.
* [monasca-common-testing](https://github.com/stackforge/monasca-common/tree/master/java/monasca-common-testing) - A set of testing related dependencies.
* [monasca-common-util](https://github.com/stackforge/monasca-common/tree/master/java/monasca-common-util) - Various utilities such as for serialization, dependency injection, date and time, invocation retries, concurrency, etc.
-
+* [monasca-common-hibernate](https://github.com/stackforge/monasca-common/tree/master/java/monasca-common-hibernate) - Hibernate based model of Monasca SQL schema
python monasca-common
======================
diff --git a/java/monasca-common-hibernate/pom.xml b/java/monasca-common-hibernate/pom.xml
index fc0c6b36..82f99536 100644
--- a/java/monasca-common-hibernate/pom.xml
+++ b/java/monasca-common-hibernate/pom.xml
@@ -10,9 +10,7 @@
jar
- 4.3.10.Final
- 3.2.0.GA
- 2.8.1
+ 5.0.1.Final
@@ -20,27 +18,12 @@
monasca-common
monasca-common-model
${project.version}
-
-
- joda-time
- joda-time
-
-
org.hibernate
hibernate-core
${hibernate-core.version}
-
- org.jadira.usertype
- usertype.core
- ${usertype.core.version}
-
-
- joda-time
- joda-time
- ${joda-time.version}
-
+
diff --git a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractAuditablePersistable.java b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractAuditablePersistable.java
index db024477..eccd72b2 100644
--- a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractAuditablePersistable.java
+++ b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractAuditablePersistable.java
@@ -16,7 +16,9 @@
package monasca.common.hibernate.db;
import java.io.Serializable;
+import java.util.Date;
+import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
@@ -24,43 +26,38 @@ import javax.persistence.Version;
import com.google.common.base.Objects;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
-import org.hibernate.annotations.Parameter;
-import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import monasca.common.hibernate.core.AuditablePersistable;
+/**
+ * Abstract implementation for {@link AuditablePersistable}.
+ *
+ * Defines auditable information such as:
+ *
+ * - {@link #createdAt} - creation date for entity
+ * - {@link #updatedAt} - last update date for entity
+ *
+ *
+ * @param primary key type
+ *
+ * @see AbstractPersistable
+ */
@DynamicInsert
@DynamicUpdate
@MappedSuperclass
abstract class AbstractAuditablePersistable
extends AbstractPersistable
implements AuditablePersistable {
- static final String DATE_TIME_TYPE = "org.jadira.usertype.dateandtime.joda.PersistentDateTime";
- static final String DB_ZONE = "UTC";
- static final String JAVA_ZONE = "jvm";
private static final long serialVersionUID = 2335373173379564615L;
@Column(name = "created_at", nullable = false)
- @Type(
- type = DATE_TIME_TYPE,
- parameters = {
- @Parameter(name = "databaseZone", value = DB_ZONE),
- @Parameter(name = "javaZone", value = JAVA_ZONE)
- }
- )
- private DateTime createdAt;
+ private Date createdAt;
@Version
@Column(name = "updated_at", nullable = false)
- @Type(
- type = DATE_TIME_TYPE,
- parameters = {
- @Parameter(name = "databaseZone", value = DB_ZONE),
- @Parameter(name = "javaZone", value = JAVA_ZONE)
- }
- )
- private DateTime updatedAt;
+ private Date updatedAt;
AbstractAuditablePersistable() {
this(null, null, null);
@@ -77,47 +74,79 @@ abstract class AbstractAuditablePersistable
this.setDates(createdAt, updatedAt);
}
+ /**
+ * Null-safe method that transform {@link DateTime} into plain {@link java.util.Date}
+ *
+ * @param value DateTime instance
+ *
+ * @return plain Date
+ *
+ * @see #nullSafeGetDate(Date)
+ */
+ static Date nullSafeSetDate(@Nullable final DateTime value) {
+ if (value == null) {
+ return null;
+ }
+ return value.toDateTime(DateTimeZone.UTC).toDate();
+ }
+
+ /**
+ * Null-safe method that transform {@link Date} into plain {@link DateTime}
+ *
+ * @param value Date instance
+ *
+ * @return DateTime
+ *
+ * @see #nullSafeSetDate(DateTime)
+ */
+ static DateTime nullSafeGetDate(@Nullable final Date value) {
+ if (value == null) {
+ return null;
+ }
+ return new DateTime(value.getTime(), DateTimeZone.UTC);
+ }
+
@Override
public DateTime getCreatedAt() {
- return createdAt;
+ return nullSafeGetDate(this.createdAt);
}
@Override
public AuditablePersistable setCreatedAt(DateTime createdAt) {
- this.createdAt = createdAt;
+ this.createdAt = nullSafeSetDate(createdAt);
return this;
}
@Override
public DateTime getUpdatedAt() {
- return updatedAt;
+ return nullSafeGetDate(this.updatedAt);
}
@Override
public AuditablePersistable setUpdatedAt(DateTime updatedAt) {
- this.updatedAt = updatedAt;
+ this.updatedAt = nullSafeSetDate(updatedAt);
return this;
}
/**
* Ensures that both {@link #createdAt} and {@link #updatedAt} will be
- * set to the earliest possible value in case passssed values are {@code NULL}
+ * set to the earliest possible value in case passed values are {@code NULL}
*
* @param createdAt created date
* @param updatedAt updated date
*/
private void setDates(final DateTime createdAt,
final DateTime updatedAt) {
- if (createdAt == null && updatedAt == null) {
- this.updatedAt = DateTime.now();
- this.createdAt = DateTime.now();
- } else if (createdAt == null) {
- this.createdAt = DateTime.now();
- } else if (updatedAt == null) {
- this.updatedAt = DateTime.now();
+ final Date date = DateTime.now(DateTimeZone.UTC).toDate();
+ if (createdAt == null) {
+ this.createdAt = date;
} else {
- this.createdAt = createdAt;
- this.updatedAt = updatedAt;
+ this.createdAt = createdAt.toDateTime(DateTimeZone.UTC).toDate();
+ }
+ if (updatedAt == null) {
+ this.updatedAt = date;
+ } else {
+ this.updatedAt = updatedAt.toDateTime(DateTimeZone.UTC).toDate();
}
}
diff --git a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractPersistable.java b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractPersistable.java
index a5c5e5a3..8807d4a8 100644
--- a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractPersistable.java
+++ b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AbstractPersistable.java
@@ -27,6 +27,17 @@ import org.hibernate.annotations.DynamicUpdate;
import monasca.common.hibernate.core.Persistable;
+/**
+ * Abstract implementation for {@link Persistable}.
+ *
+ * Defines primary key of a specific entity.
+ * Primary key may take any arbitrary type that
+ * is required by specific case.
+ *
+ * @param primary key type
+ *
+ * @see AbstractAuditablePersistable
+ */
@DynamicInsert
@DynamicUpdate
@MappedSuperclass
diff --git a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDb.java b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDb.java
index 9644d495..8165e64c 100644
--- a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDb.java
+++ b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDb.java
@@ -15,6 +15,7 @@
package monasca.common.hibernate.db;
import java.util.Collection;
+import java.util.Date;
import javax.annotation.Nullable;
import javax.persistence.CascadeType;
@@ -35,8 +36,6 @@ import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
-import org.hibernate.annotations.Parameter;
-import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import monasca.common.model.alarm.AlarmState;
@@ -76,14 +75,7 @@ public class AlarmDb
private String link;
@Column(name = "state_updated_at")
- @Type(
- type = DATE_TIME_TYPE,
- parameters = {
- @Parameter(name = "databaseZone", value = DB_ZONE),
- @Parameter(name = "javaZone", value = JAVA_ZONE)
- }
- )
- private DateTime stateUpdatedAt;
+ private Date stateUpdatedAt;
@OneToMany(mappedBy = "alarmMetricId.alarm", fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST,
@@ -119,7 +111,7 @@ public class AlarmDb
this.link = link;
this.state = state;
this.lifecycleState = lifecycleState;
- this.stateUpdatedAt = stateUpdatedAt;
+ this.setStateUpdatedAt(stateUpdatedAt);
}
public AlarmState getState() {
@@ -150,11 +142,11 @@ public class AlarmDb
}
public DateTime getStateUpdatedAt() {
- return stateUpdatedAt;
+ return nullSafeGetDate(this.stateUpdatedAt);
}
public AlarmDb setStateUpdatedAt(DateTime stateUpdatedAt) {
- this.stateUpdatedAt = stateUpdatedAt;
+ this.stateUpdatedAt = nullSafeSetDate(stateUpdatedAt);
return this;
}
diff --git a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDefinitionDb.java b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDefinitionDb.java
index bcebe36b..6753415e 100644
--- a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDefinitionDb.java
+++ b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/AlarmDefinitionDb.java
@@ -16,6 +16,7 @@ package monasca.common.hibernate.db;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import javax.annotation.Nullable;
import javax.persistence.Basic;
@@ -36,7 +37,6 @@ import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.hibernate.annotations.BatchSize;
-import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
@@ -95,14 +95,7 @@ public class AlarmDefinitionDb
private boolean actionsEnabled = DEFAULT_ACTIONS_ENABLED;
@Column(name = "deleted_at")
- @Type(
- type = DATE_TIME_TYPE,
- parameters = {
- @Parameter(name = "databaseZone", value = DB_ZONE),
- @Parameter(name = "javaZone", value = JAVA_ZONE)
- }
- )
- private DateTime deletedAt;
+ private Date deletedAt;
@BatchSize(size = 50)
@OneToMany(mappedBy = "alarmDefinition", fetch = FetchType.LAZY)
@@ -132,7 +125,7 @@ public class AlarmDefinitionDb
this.severity = severity;
this.matchBy = matchBy;
this.actionsEnabled = actionsEnabled;
- this.deletedAt = deletedAt;
+ this.setDeletedAt(deletedAt);
}
public AlarmDefinitionDb(String id,
@@ -145,7 +138,7 @@ public class AlarmDefinitionDb
}
public AlarmDefinitionDb setDeletedAt(final DateTime deletedAt) {
- this.deletedAt = deletedAt;
+ this.deletedAt = nullSafeSetDate(deletedAt);
return this;
}
@@ -220,7 +213,7 @@ public class AlarmDefinitionDb
}
public DateTime getDeletedAt() {
- return deletedAt;
+ return nullSafeGetDate(this.deletedAt);
}
public boolean hasAlarm(final AlarmDb alarm) {
diff --git a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/SubAlarmDefinitionDb.java b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/SubAlarmDefinitionDb.java
index 0a7355fc..65ba0c7c 100644
--- a/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/SubAlarmDefinitionDb.java
+++ b/java/monasca-common-hibernate/src/main/java/monasca/common/hibernate/db/SubAlarmDefinitionDb.java
@@ -24,11 +24,12 @@ import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
-import monasca.common.model.alarm.AlarmOperator;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.joda.time.DateTime;
+import monasca.common.model.alarm.AlarmOperator;
+
@Entity
@Table(name = "sub_alarm_definition")
@NamedQueries({
diff --git a/java/monasca-common-util/pom.xml b/java/monasca-common-util/pom.xml
index edbf2a8a..68c29deb 100644
--- a/java/monasca-common-util/pom.xml
+++ b/java/monasca-common-util/pom.xml
@@ -84,7 +84,7 @@
org.hibernate
hibernate-validator
- 4.3.0.Final
+ 5.2.1.Final
diff --git a/java/monasca-common-util/src/main/java/monasca/common/util/Conversions.java b/java/monasca-common-util/src/main/java/monasca/common/util/Conversions.java
index d70366f0..0b0b02d0 100644
--- a/java/monasca-common-util/src/main/java/monasca/common/util/Conversions.java
+++ b/java/monasca-common-util/src/main/java/monasca/common/util/Conversions.java
@@ -16,7 +16,17 @@
*/
package monasca.common.util;
+import java.util.Arrays;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
public final class Conversions {
+ private static final String[] SUPPORTED_VARIANT_TO_ENUM_TYPES = new String[]{
+ String.class.getSimpleName(),
+ Number.class.getSimpleName(),
+ Enum.class.getSimpleName()
+ };
/**
* Converts a Java Object of type Number to an Integer
@@ -32,4 +42,91 @@ public final class Conversions {
variant.getClass(), variant));
}
}
-}
\ No newline at end of file
+
+ /**
+ * Converts a Java Object to DateTime instance
+ *
+ * @param variant object of type supported in {@link org.joda.time.convert.ConverterManager}
+ *
+ * @return DateTime in {@link DateTimeZone#UTC}
+ *
+ * @throws IllegalArgumentException
+ * @see #variantToDateTime(Object, DateTimeZone)
+ * @see DateTime
+ * @see DateTimeZone#UTC
+ */
+ public static DateTime variantToDateTime(final Object variant) {
+ return variantToDateTime(variant, DateTimeZone.UTC);
+ }
+
+
+ /**
+ * Converts a Java Object to DateTime instance using given {@code timeZone}
+ *
+ * @param variant object of type supported in {@link org.joda.time.convert.ConverterManager}
+ * @param timeZone timeZone to be used
+ *
+ * @return DateTime in {@code timeZone}
+ *
+ * @throws IllegalArgumentException
+ * @see #variantToDateTime(Object)
+ * @see DateTime
+ * @see DateTimeZone
+ */
+ public static DateTime variantToDateTime(final Object variant, final DateTimeZone timeZone) {
+ if (variant instanceof DateTime) {
+ return ((DateTime) variant).toDateTime(timeZone);
+ }
+ return new DateTime(variant, timeZone);
+ }
+
+ /**
+ * Converts variant to {@code enumClazz} instance.
+ *
+ * Supported variants are:
+ *
+ * - {@link String}, trimmed and upper-cased
+ * - {@link Number}, taken from {@link Class#getEnumConstants}
+ * - {@link Enum}, simple cast
+ *
+ *
+ * @param variant object of type supported by this method, see above
+ * @param enumClazz desired {@link Enum}
+ * @param enumType of {@code enumClazz}
+ *
+ * @return valid enum class instance
+ *
+ * @throws IllegalArgumentException
+ */
+ @SuppressWarnings("unchecked")
+ public static > T variantToEnum(final Object variant, final Class enumClazz) {
+ if (variant == null) {
+ return null;
+ }
+
+ if (variant instanceof String) {
+ return Enum.valueOf(enumClazz, ((String) variant).trim().toUpperCase());
+ } else if (variant instanceof Number) {
+ final Integer index = variantToInteger(variant);
+ final T[] enumConstants = enumClazz.getEnumConstants();
+ if (index < 0 || index >= enumConstants.length) {
+ throw new IllegalArgumentException(
+ String.format("Variant of type \"%s\", and value \"%s\" is out of range [, %d]",
+ variant.getClass(),
+ variant,
+ enumConstants.length
+ )
+ );
+ }
+
+ return enumConstants[index];
+ } else if (variant instanceof Enum) {
+ return (T) variant;
+ }
+
+ throw new IllegalArgumentException(String.format("\"%s\", and value \"%s\" is not one of %s",
+ variant.getClass(), variant, Arrays.toString(SUPPORTED_VARIANT_TO_ENUM_TYPES)));
+
+ }
+
+}
diff --git a/java/monasca-common-util/src/test/java/monasca/common/util/ConversionsTest.java b/java/monasca-common-util/src/test/java/monasca/common/util/ConversionsTest.java
index 8dfc5cb3..52fe16ff 100644
--- a/java/monasca-common-util/src/test/java/monasca/common/util/ConversionsTest.java
+++ b/java/monasca-common-util/src/test/java/monasca/common/util/ConversionsTest.java
@@ -14,12 +14,15 @@
package monasca.common.util;
import static org.testng.Assert.assertEquals;
-
-import org.testng.annotations.Test;
-
+import static org.testng.Assert.assertNotEquals;
import java.math.BigDecimal;
+import com.beust.jcommander.internal.Lists;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.annotations.Test;
+
@Test
public class ConversionsTest {
public void testInteger() {
@@ -49,4 +52,63 @@ public class ConversionsTest {
public void testObject() {
Conversions.variantToInteger(new Object());
}
+
+ public void testDateTimeShouldNotEqualDifferentTZExplicit() {
+ final DateTime now = DateTime.now(DateTimeZone.UTC);
+ assertNotEquals(now, Conversions.variantToDateTime(now, DateTimeZone.forOffsetHours(2)));
+ }
+
+ public void testDateTimeShouldNotEqualDifferentTZImplicit() {
+ final DateTime now = DateTime.now();
+ assertNotEquals(now, Conversions.variantToDateTime(now));
+ }
+
+ public void testDateTimeShouldEqualSameTZImplicit() {
+ final DateTime now = DateTime.now(DateTimeZone.UTC);
+ assertEquals(now, Conversions.variantToDateTime(now));
+ }
+
+ public void testDateTimeShouldEqualSameTZExplicit() {
+ final DateTime now = DateTime.now(DateTimeZone.UTC);
+ assertEquals(now, Conversions.variantToDateTime(now, DateTimeZone.UTC));
+ }
+
+ public void testEnumFromString() {
+ assertEquals(MockEnum.THIS, Conversions.variantToEnum("THIS", MockEnum.class));
+ }
+
+ public void testEnumFromStringLowerCased() {
+ assertEquals(MockEnum.THIS, Conversions.variantToEnum("this", MockEnum.class));
+ }
+
+ public void testEnumFromStringWithSpaces() {
+ assertEquals(MockEnum.THIS, Conversions.variantToEnum(" THIS ", MockEnum.class));
+ }
+
+ public void testEnumFromNumber() {
+ assertEquals(MockEnum.IS, Conversions.variantToEnum(1, MockEnum.class));
+ }
+
+ public void testEnumFromNumberDouble() {
+ assertEquals(MockEnum.IS, Conversions.variantToEnum(1.0, MockEnum.class));
+ }
+
+ public void testEnumFromEnum() {
+ assertEquals(MockEnum.TEST, Conversions.variantToEnum(MockEnum.TEST, MockEnum.class));
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testEnumShouldFailUnsupportedType(){
+ Conversions.variantToEnum(Lists.newArrayList(),MockEnum.class);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testEnumShouldFailInvalidEnumIndex(){
+ final int invalidIndex = MockEnum.class.getEnumConstants().length + 1;
+ Conversions.variantToEnum(invalidIndex, MockEnum.class);
+ }
+
+ private enum MockEnum {
+ THIS,IS,TEST
+ }
}