diff --git a/src/main/java/com/avaje/ebean/annotation/WhoCreated.java b/src/main/java/com/avaje/ebean/annotation/WhoCreated.java new file mode 100644 index 000000000..c9dcfdfae --- /dev/null +++ b/src/main/java/com/avaje/ebean/annotation/WhoCreated.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Mapped onto a entity bean property that represents the user id of + * who created the entity> + *

+ * To use this annotation you need to implement CurrentUserProvider. + * The type of the bean property should match the type returned by + * CurrentUserProvider. + *

+ + *

Example:

+ *
{@code
+ *
+ *   @WhoCreated
+ *   String whoCreated;
+ *
+ * }
+ */ +@Target({ ElementType.FIELD, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface WhoCreated { + +} diff --git a/src/main/java/com/avaje/ebean/annotation/WhoModified.java b/src/main/java/com/avaje/ebean/annotation/WhoModified.java new file mode 100644 index 000000000..1ef57e298 --- /dev/null +++ b/src/main/java/com/avaje/ebean/annotation/WhoModified.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Mapped onto a entity bean property that represents the user id of + * who last modified the entity> + *

+ * To use this annotation you need to implement CurrentUserProvider. + * The type of the bean property should match the type returned by + * CurrentUserProvider. + *

+ * + *

Example:

+ *
{@code
+ *
+ *   @WhoModified
+ *   String whoModified;
+ *
+ * }
+ */ +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface WhoModified { + +} diff --git a/src/main/java/com/avaje/ebean/config/CurrentUserProvider.java b/src/main/java/com/avaje/ebean/config/CurrentUserProvider.java new file mode 100644 index 000000000..3980cd163 --- /dev/null +++ b/src/main/java/com/avaje/ebean/config/CurrentUserProvider.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.config; + +/** + * Provides the current user in order to support 'Who created', 'Who modified' and other audit features. + */ +public interface CurrentUserProvider { + + /** + * Return the current user id. + *

+ * The type returned should match the type of the properties annotated + * with @WhoCreated and @WhoModified. These are typically String, Long or UUID. + *

+ */ + Object currentUser(); +} diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 4f234a425..594fd8009 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -113,6 +113,12 @@ public class ServerConfig { */ private String classPathReaderClassName; + /** + * This is used to populate @WhoCreated, @WhoModified and + * support other audit features (who executed a query etc). + */ + private CurrentUserProvider currentUserProvider; + /** * Config controlling the autofetch behaviour. */ @@ -446,6 +452,22 @@ public class ServerConfig { this.defaultServer = defaultServer; } + /** + * Return the CurrentUserProvider. This is used to populate @WhoCreated, @WhoModified and + * support other audit features (who executed a query etc). + */ + public CurrentUserProvider getCurrentUserProvider() { + return currentUserProvider; + } + + /** + * Set the CurrentUserProvider. This is used to populate @WhoCreated, @WhoModified and + * support other audit features (who executed a query etc). + */ + public void setCurrentUserProvider(CurrentUserProvider currentUserProvider) { + this.currentUserProvider = currentUserProvider; + } + /** * Return the PersistBatch mode to use by default at the transaction level. *

@@ -1812,6 +1834,7 @@ public class ServerConfig { autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode); useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager); + currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider"); disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch); databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform"); encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager"); diff --git a/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java b/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java index 75013143d..917b7bb65 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java @@ -47,6 +47,13 @@ public class ClassUtil { return classLoader; } + /** + * Return true if javax validation annotations like Size and NotNull are present. + */ + public static boolean isJavaxValidationAnnotationsPresent() { + return isPresent("javax.validation.constraints.NotNull", null); + } + /** * Return true if Jackson annotations like JsonIgnore are present. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index 74a56c0a5..c751d53de 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.core; import javax.sql.DataSource; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,7 +93,7 @@ public class InternalConfiguration { private final XmlConfig xmlConfig; private final JsonFactory jsonFactory; - + public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor, ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) { @@ -267,4 +268,7 @@ public class InternalConfiguration { return backgroundExecutor; } + public GeneratedPropertyFactory getGeneratedPropertyFactory() { + return new GeneratedPropertyFactory(serverConfig.getCurrentUserProvider()); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java index 00bf46624..a6d294c0c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -154,7 +154,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap { this.eagerFetchLobs = serverConfig.isEagerFetchLobs(); this.asOfViewSuffix = serverConfig.getAsOfViewSuffix(); - this.readAnnotations = new ReadAnnotations(asOfViewSuffix); + this.readAnnotations = new ReadAnnotations(config.getGeneratedPropertyFactory(), asOfViewSuffix); this.bootupClasses = config.getBootupClasses(); this.createProperties = config.getDeployCreateProperties(); this.typeManager = config.getTypeManager(); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java index 30e6a8be1..bdf6b6173 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java @@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty; import java.math.BigDecimal; import java.util.HashSet; +import com.avaje.ebean.config.CurrentUserProvider; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; /** @@ -10,19 +11,27 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; */ public class GeneratedPropertyFactory { - CounterFactory counterFactory; + private final CounterFactory counterFactory = new CounterFactory();; - InsertTimestampFactory insertFactory; + private final InsertTimestampFactory insertFactory = new InsertTimestampFactory(); - UpdateTimestampFactory updateFactory; + private final UpdateTimestampFactory updateFactory = new UpdateTimestampFactory(); - HashSet numberTypes = new HashSet(); + private final HashSet numberTypes = new HashSet(); - public GeneratedPropertyFactory() { - counterFactory = new CounterFactory(); - insertFactory = new InsertTimestampFactory(); - updateFactory = new UpdateTimestampFactory(); - + private final GeneratedWhoModified generatedWhoModified; + + private final GeneratedWhoCreated generatedWhoCreated; + + public GeneratedPropertyFactory(CurrentUserProvider currentUserProvider) { + + if (currentUserProvider != null) { + generatedWhoCreated = new GeneratedWhoCreated(currentUserProvider); + generatedWhoModified = new GeneratedWhoModified(currentUserProvider); + } else { + generatedWhoCreated = null; + generatedWhoModified = null; + } numberTypes.add(Integer.class.getName()); numberTypes.add(int.class.getName()); @@ -62,4 +71,18 @@ public class GeneratedPropertyFactory { updateFactory.setUpdateTimestamp(property); } + public void setWhoCreated(DeployBeanProperty property) { + if (generatedWhoCreated == null) { + throw new IllegalStateException("No CurrentUserProvider has been set so @WhoCreated is not supported"); + } + property.setGeneratedProperty(generatedWhoCreated); + } + + public void setWhoModified(DeployBeanProperty property) { + if (generatedWhoModified == null) { + throw new IllegalStateException("No CurrentUserProvider has been set so @WhoModified is not supported"); + } + property.setGeneratedProperty(generatedWhoModified); + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedWhoCreated.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedWhoCreated.java new file mode 100644 index 000000000..c0b9fb950 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedWhoCreated.java @@ -0,0 +1,47 @@ +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.CurrentUserProvider; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to populate @WhoCreated bean properties. + */ +public class GeneratedWhoCreated implements GeneratedProperty { + + final CurrentUserProvider currentUserProvider; + + public GeneratedWhoCreated(CurrentUserProvider currentUserProvider) { + this.currentUserProvider = currentUserProvider; + } + + @Override + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return currentUserProvider.currentUser(); + } + + @Override + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return null; + } + + @Override + public boolean includeInUpdate() { + return false; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + @Override + public boolean includeInInsert() { + return true; + } + + @Override + public boolean isDDLNotNullable() { + return true; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedWhoModified.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedWhoModified.java new file mode 100644 index 000000000..dbfc93896 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedWhoModified.java @@ -0,0 +1,47 @@ +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.CurrentUserProvider; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to populate @WhoModified bean properties. + */ +public class GeneratedWhoModified implements GeneratedProperty { + + final CurrentUserProvider currentUserProvider; + + public GeneratedWhoModified(CurrentUserProvider currentUserProvider) { + this.currentUserProvider = currentUserProvider; + } + + @Override + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return currentUserProvider.currentUser(); + } + + @Override + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return currentUserProvider.currentUser(); + } + + @Override + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return true; + } + + @Override + public boolean includeInInsert() { + return true; + } + + @Override + public boolean isDDLNotNullable() { + return true; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 665dff5b6..24e81b840 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -34,8 +34,8 @@ public class AnnotationAssocManys extends AnnotationParser { /** * Create with the DeployInfo. */ - public AnnotationAssocManys(DeployBeanInfo info, BeanDescriptorManager factory) { - super(info); + public AnnotationAssocManys(DeployBeanInfo info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) { + super(info, javaxValidationAnnotations); this.factory = factory; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index 5bbcfea64..c1ade4105 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -33,8 +33,8 @@ public class AnnotationAssocOnes extends AnnotationParser { /** * Create with the deploy Info. */ - public AnnotationAssocOnes(DeployBeanInfo info, BeanDescriptorManager factory) { - super(info); + public AnnotationAssocOnes(DeployBeanInfo info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) { + super(info, javaxValidationAnnotations); this.factory = factory; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java index 7f4dc21b1..8b57bce76 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -29,8 +29,8 @@ public class AnnotationClass extends AnnotationParser { private final String asOfViewSuffix; - public AnnotationClass(DeployBeanInfo info, String asOfViewSuffix) { - super(info); + public AnnotationClass(DeployBeanInfo info, boolean validationAnnotations, String asOfViewSuffix) { + super(info, validationAnnotations); this.asOfViewSuffix = asOfViewSuffix; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java index 4bad6c26c..4d926ad87 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -1,25 +1,11 @@ package com.avaje.ebeaninternal.server.deploy.parse; -import com.avaje.ebean.annotation.CreatedTimestamp; -import com.avaje.ebean.annotation.DbHstore; -import com.avaje.ebean.annotation.DbJson; -import com.avaje.ebean.annotation.DbJsonB; -import com.avaje.ebean.annotation.EmbeddedColumns; -import com.avaje.ebean.annotation.Encrypted; -import com.avaje.ebean.annotation.Expose; -import com.avaje.ebean.annotation.Formula; -import com.avaje.ebean.annotation.HistoryExclude; -import com.avaje.ebean.annotation.Index; -import com.avaje.ebean.annotation.JsonIgnore; -import com.avaje.ebean.annotation.UpdatedTimestamp; -import com.avaje.ebean.annotation.WhenCreated; -import com.avaje.ebean.annotation.WhenModified; +import com.avaje.ebean.annotation.*; import com.avaje.ebean.config.EncryptDeploy; import com.avaje.ebean.config.EncryptDeploy.Mode; import com.avaje.ebean.config.dbplatform.DbEncrypt; import com.avaje.ebean.config.dbplatform.DbEncryptFunction; import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebeaninternal.api.ClassUtil; import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; @@ -49,17 +35,22 @@ public class AnnotationFields extends AnnotationParser { /** * If present read Jackson JsonIgnore. */ - private boolean jacksonAnnotationsPresent = ClassUtil.isJacksonAnnotationsPresent(); + private final boolean jacksonAnnotationsPresent; + + private final GeneratedPropertyFactory generatedPropFactory; /** * By default we lazy load Lob properties. */ private FetchType defaultLobFetchType = FetchType.LAZY; - private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory(); + public AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo info, + boolean javaxValidationAnnotations, boolean jacksonAnnotationsPresent, boolean eagerFetchLobs) { - public AnnotationFields(DeployBeanInfo info, boolean eagerFetchLobs) { - super(info); + super(info, javaxValidationAnnotations); + + this.jacksonAnnotationsPresent = jacksonAnnotationsPresent; + this.generatedPropFactory = generatedPropFactory; if (eagerFetchLobs) { defaultLobFetchType = FetchType.EAGER; @@ -218,6 +209,13 @@ public class AnnotationFields extends AnnotationParser { generatedPropFactory.setUpdateTimestamp(prop); } + if (get(prop, WhoCreated.class) != null) { + generatedPropFactory.setWhoCreated(prop); + } + if (get(prop, WhoModified.class) != null) { + generatedPropFactory.setWhoModified(prop); + } + if (get(prop, HistoryExclude.class) != null) { prop.setExcludedFromHistory(true); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java index d36d17cc0..ed71b2e93 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java @@ -21,22 +21,14 @@ public abstract class AnnotationParser extends AnnotationBase { protected final Class beanType; - protected boolean validationAnnotations; + protected final boolean validationAnnotations; - public AnnotationParser(DeployBeanInfo info) { + public AnnotationParser(DeployBeanInfo info, boolean validationAnnotations) { super(info.getUtil()); + this.validationAnnotations = validationAnnotations; this.info = info; this.beanType = info.getDescriptor().getBeanType(); this.descriptor = info.getDescriptor(); - - try { - Class.forName("javax.validation.constraints.NotNull"); - validationAnnotations = true; - } catch (ClassNotFoundException e) { - // javax.validation not in the classpath so don't - // check for NotNull and Size - validationAnnotations = false; - } } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java index f2191918e..4559f93d5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java @@ -9,8 +9,8 @@ import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; */ public class AnnotationSql extends AnnotationParser { - public AnnotationSql(DeployBeanInfo info) { - super(info); + public AnnotationSql(DeployBeanInfo info, boolean javaxValidationAnnotations) { + super(info, javaxValidationAnnotations); } public void parse() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java index 2ca913b53..33ec3a759 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java @@ -1,6 +1,8 @@ package com.avaje.ebeaninternal.server.deploy.parse; +import com.avaje.ebeaninternal.api.ClassUtil; import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; /** @@ -8,61 +10,76 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; */ public class ReadAnnotations { + /** + * Creates appropriate generated properties - WhenXXX, WhoXXX, Version etc. + */ + private final GeneratedPropertyFactory generatedPropFactory; + /** * Typically _with_history and when appended to the base table derives the name of * the view that unions the base table with the history table to support asOf queries. */ private final String asOfViewSuffix; - public ReadAnnotations(String asOfViewSuffix) { + /** + * True if the javax validation annotations are present in the classpath. + */ + private final boolean javaxValidationAnnotations; + + /** + * True if the jackson annotations are present in the classpath. + */ + private final boolean jacksonAnnotations; + + public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix) { + this.generatedPropFactory = generatedPropFactory; this.asOfViewSuffix = asOfViewSuffix; + this.javaxValidationAnnotations = ClassUtil.isJavaxValidationAnnotationsPresent(); + this.jacksonAnnotations = ClassUtil.isJacksonAnnotationsPresent(); } /** - * Read the initial non-relationship annotations included Id and EmbeddedId. - *

- * We then have enough to create BeanTables which are used in readAssociations - * to resolve the relationships etc. - *

- */ - public void readInitial(DeployBeanInfo info, boolean eagerFetchLobs){ + * Read the initial non-relationship annotations included Id and EmbeddedId. + *

+ * We then have enough to create BeanTables which are used in readAssociations + * to resolve the relationships etc. + *

+ */ + public void readInitial(DeployBeanInfo info, boolean eagerFetchLobs) { - try { - new AnnotationClass(info, asOfViewSuffix).parse(); - new AnnotationFields(info, eagerFetchLobs).parse(); - - } catch (RuntimeException e){ - String msg = "Error reading annotations for "+info; - throw new RuntimeException(msg, e); - } - } - + try { + new AnnotationClass(info, javaxValidationAnnotations, asOfViewSuffix).parse(); + new AnnotationFields(generatedPropFactory, info, javaxValidationAnnotations, jacksonAnnotations, eagerFetchLobs).parse(); - /** - * Read and process the associated relationship annotations. - *

- * These can only be processed after the BeanTables have been created - *

- *

- * This uses the factory as a call back to get the BeanTable for a given - * associated bean. - *

- */ - public void readAssociations(DeployBeanInfo info, BeanDescriptorManager factory){ - - try { - - new AnnotationAssocOnes(info, factory).parse(); - new AnnotationAssocManys(info, factory).parse(); - - // read the Sql annotations last because they may be - // dependent on field level annotations - new AnnotationSql(info).parse(); - - } catch (RuntimeException e){ - String msg = "Error reading annotations for "+info; - throw new RuntimeException(msg, e); - } + } catch (RuntimeException e) { + String msg = "Error reading annotations for " + info; + throw new RuntimeException(msg, e); } - + } + + /** + * Read and process the associated relationship annotations. + *

+ * These can only be processed after the BeanTables have been created + *

+ *

+ * This uses the factory as a call back to get the BeanTable for a given + * associated bean. + *

+ */ + public void readAssociations(DeployBeanInfo info, BeanDescriptorManager factory) { + + try { + + new AnnotationAssocOnes(info, javaxValidationAnnotations, factory).parse(); + new AnnotationAssocManys(info, javaxValidationAnnotations, factory).parse(); + + // read the Sql annotations last because they may be + // dependent on field level annotations + new AnnotationSql(info, javaxValidationAnnotations).parse(); + + } catch (RuntimeException e) { + throw new RuntimeException("Error reading annotations for " + info, e); + } + } } diff --git a/src/test/java/com/avaje/ebean/MyCurrentUserProvider.java b/src/test/java/com/avaje/ebean/MyCurrentUserProvider.java new file mode 100644 index 000000000..3c28a9651 --- /dev/null +++ b/src/test/java/com/avaje/ebean/MyCurrentUserProvider.java @@ -0,0 +1,32 @@ +package com.avaje.ebean; + +import com.avaje.ebean.config.CurrentUserProvider; + +/** + * Returns the current user typically from a Thread local or similar context. + */ +public class MyCurrentUserProvider implements CurrentUserProvider { + + public static final String DEFAULT = "42789"; + + /** + * Do not do this yourself - this is for testing purposes. + */ + private static String userId = DEFAULT; + + + @Override + public Object currentUser() { + // just hardcoding here for testing + return userId; + } + + public static void setUserId(String value) { + userId = value; + } + + public static void resetToDefault() { + userId = DEFAULT; + } + +} diff --git a/src/test/java/com/avaje/tests/generated/TestGeneratedWho.java b/src/test/java/com/avaje/tests/generated/TestGeneratedWho.java new file mode 100644 index 000000000..f10088dbd --- /dev/null +++ b/src/test/java/com/avaje/tests/generated/TestGeneratedWho.java @@ -0,0 +1,46 @@ +package com.avaje.tests.generated; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.MyCurrentUserProvider; +import com.avaje.tests.model.EWhoProps; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class TestGeneratedWho extends BaseTestCase { + + @Test + public void insertUpdate() { + + EWhoProps bean = new EWhoProps(); + bean.setName("one"); + + MyCurrentUserProvider.setUserId("INSERT_WHO_1"); + Ebean.save(bean); + + assertEquals("one", bean.getName()); + assertEquals("INSERT_WHO_1", bean.getWhoCreated()); + assertEquals("INSERT_WHO_1", bean.getWhoModified()); + assertNotNull(bean.getWhenCreated()); + assertNotNull(bean.getWhenModified()); + + MyCurrentUserProvider.setUserId("UPDATE_WHO_1"); + bean.setName("two"); + Ebean.save(bean); + + assertEquals("two", bean.getName()); + assertEquals("INSERT_WHO_1", bean.getWhoCreated()); + assertEquals("UPDATE_WHO_1", bean.getWhoModified()); + + MyCurrentUserProvider.setUserId("UPDATE_WHO_2"); + bean.setName("three"); + Ebean.save(bean); + + assertEquals("INSERT_WHO_1", bean.getWhoCreated()); + assertEquals("UPDATE_WHO_2", bean.getWhoModified()); + + MyCurrentUserProvider.resetToDefault(); + } +} diff --git a/src/test/java/com/avaje/tests/model/EWhoProps.java b/src/test/java/com/avaje/tests/model/EWhoProps.java new file mode 100644 index 000000000..5d9c2504d --- /dev/null +++ b/src/test/java/com/avaje/tests/model/EWhoProps.java @@ -0,0 +1,91 @@ +package com.avaje.tests.model; + +import com.avaje.ebean.annotation.WhenCreated; +import com.avaje.ebean.annotation.WhenModified; +import com.avaje.ebean.annotation.WhoCreated; +import com.avaje.ebean.annotation.WhoModified; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; +import java.sql.Timestamp; + +@Entity +public class EWhoProps { + + @Id + Long id; + + String name; + + @Version + Long version; + + @WhenCreated + Timestamp whenCreated; + + @WhenModified + Timestamp whenModified; + + @WhoCreated + String whoCreated; + + @WhoModified + String whoModified; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public Timestamp getWhenCreated() { + return whenCreated; + } + + public void setWhenCreated(Timestamp whenCreated) { + this.whenCreated = whenCreated; + } + + public Timestamp getWhenModified() { + return whenModified; + } + + public void setWhenModified(Timestamp whenModified) { + this.whenModified = whenModified; + } + + public String getWhoCreated() { + return whoCreated; + } + + public void setWhoCreated(String whoCreated) { + this.whoCreated = whoCreated; + } + + public String getWhoModified() { + return whoModified; + } + + public void setWhoModified(String whoModified) { + this.whoModified = whoModified; + } +} diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 0d9465e2d..68fa9c44f 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -27,6 +27,7 @@ ebean.ddl.run=true ebean.persistBatch=NONE ebean.persistBatchOnCascade=ALL +ebean.currentUserProvider=com.avaje.ebean.MyCurrentUserProvider #ebean.jsonInclude=NON_NULL