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