From fc30e21e5ce7ac3eb4c98446b64c8481f573a880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Tr=C4=99bski?= Date: Fri, 4 Sep 2015 09:51:28 +0200 Subject: [PATCH] Time zone fix - removed unused dependencies - matched joda to entire monasca - switched to standard java date + added explicit UTC Change-Id: I04e986097010792aed772e83e529edb6d88a2125 --- README.md | 2 +- java/monasca-common-hibernate/pom.xml | 21 +--- .../db/AbstractAuditablePersistable.java | 99 ++++++++++++------- .../hibernate/db/AbstractPersistable.java | 11 +++ .../monasca/common/hibernate/db/AlarmDb.java | 18 +--- .../hibernate/db/AlarmDefinitionDb.java | 17 +--- .../hibernate/db/SubAlarmDefinitionDb.java | 3 +- java/monasca-common-util/pom.xml | 2 +- .../java/monasca/common/util/Conversions.java | 99 ++++++++++++++++++- .../monasca/common/util/ConversionsTest.java | 68 ++++++++++++- 10 files changed, 254 insertions(+), 86 deletions(-) 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: + *
    + *
  1. {@link #createdAt} - creation date for entity
  2. + *
  3. {@link #updatedAt} - last update date for entity
  4. + *
+ * + * @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: + *
    + *
  1. {@link String}, trimmed and upper-cased
  2. + *
  3. {@link Number}, taken from {@link Class#getEnumConstants}
  4. + *
  5. {@link Enum}, simple cast
  6. + *
+ * + * @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 + } }