startupInstances) {
- if (startupInstances != null) {
- for (ServerConfigStartup l : startupInstances) {
- this.serverConfigStartupInstances.add(l);
- // don't automatically instantiate
- this.serverConfigStartupList.remove(l.getClass());
- }
- }
+ add(startupInstances, serverConfigStartupInstances, serverConfigStartupCandidates);
}
public void addChangeLogInstances(ServerConfig serverConfig) {
readAuditPrepare = serverConfig.getReadAuditPrepare();
readAuditLogger = serverConfig.getReadAuditLogger();
-
- if (readAuditPrepare == null && readAuditPrepareClass != null) {
- readAuditPrepare = (ReadAuditPrepare)create(readAuditPrepareClass, false);
- }
- if (readAuditLogger == null && readAuditLoggerClass != null) {
- readAuditLogger = (ReadAuditLogger)create(readAuditLoggerClass, false);
- }
-
+ changeLogPrepare = serverConfig.getChangeLogPrepare();
changeLogListener = serverConfig.getChangeLogListener();
changeLogRegister = serverConfig.getChangeLogRegister();
- changeLogPrepare = serverConfig.getChangeLogPrepare();
// if not already set create the implementations found
// via classpath scanning
+ if (readAuditPrepare == null && readAuditPrepareClass != null) {
+ readAuditPrepare = create(readAuditPrepareClass, false);
+ }
+ if (readAuditLogger == null && readAuditLoggerClass != null) {
+ readAuditLogger = create(readAuditLoggerClass, false);
+ }
if (changeLogPrepare == null && changeLogPrepareClass != null) {
- changeLogPrepare = (ChangeLogPrepare)create(changeLogPrepareClass, false);
+ changeLogPrepare = create(changeLogPrepareClass, false);
}
if (changeLogListener == null && changeLogListenerClass != null) {
- changeLogListener = (ChangeLogListener)create(changeLogListenerClass, false);
+ changeLogListener = create(changeLogListenerClass, false);
}
if (changeLogRegister == null && changeLogRegisterClass != null) {
- changeLogRegister = (ChangeLogRegister)create(changeLogRegisterClass, false);
+ changeLogRegister = create(changeLogRegisterClass, false);
}
}
@@ -233,10 +231,10 @@ public class BootupClasses implements ClassFilter {
*
* Use logOnException = true to log the error and carry on.
*/
- private Object create(Class> cls, boolean logOnException) {
+ private T create(Class cls, boolean logOnException) {
try {
// instantiate via found class
- Constructor constructor = cls.getConstructor();
+ Constructor constructor = cls.getConstructor();
return constructor.newInstance();
} catch (NoSuchMethodException e) {
@@ -258,13 +256,18 @@ public class BootupClasses implements ClassFilter {
/**
* Create the instance if it has a default constructor and add it to the list of instances.
+ * It clears the list of classes afterwards, so that each class in the given list is
+ * instantiated only once
*/
- @SuppressWarnings(value = "unchecked")
- private void createAdd(Class> cls, List instances) {
- Object newInstance = create(cls, true);
- if (newInstance != null) {
- instances.add((T)newInstance);
- }
+ private List createAdd(List instances, List> candidates) {
+ for (Class extends T> cls : candidates) {
+ T newInstance = create(cls, true);
+ if (newInstance != null) {
+ instances.add(newInstance);
+ }
+ }
+ candidates.clear(); // important, clear class list!
+ return instances;
}
public ChangeLogPrepare getChangeLogPrepare() {
@@ -286,54 +289,36 @@ public class BootupClasses implements ClassFilter {
public ReadAuditLogger getReadAuditLogger() {
return readAuditLogger;
}
-
- public List getBeanQueryAdapters() {
- // add class registered BeanQueryAdapter to the already created instances
- for (Class> cls : beanQueryAdapterList) {
- createAdd(cls, queryAdapterInstances);
- }
- return queryAdapterInstances;
+
+
+ public List getIdGenerators() {
+ return createAdd(idGeneratorInstances, idGeneratorCandidates);
}
-
- public List getBeanFindControllers() {
- // add class registered BeanFindController to the list of created instances
- for (Class> cls : beanFindControllerList) {
- createAdd(cls, findControllerInstances);
- }
- return findControllerInstances;
- }
-
- public List getBeanPersistListeners() {
- // add class registered BeanPersistController to the already created instances
- for (Class> cls : beanListenerList) {
- createAdd(cls, persistListenerInstances);
- }
- return persistListenerInstances;
- }
-
+
public List getBeanPersistControllers() {
- // add class registered BeanPersistController to the already created instances
- for (Class> cls : beanControllerList) {
- createAdd(cls, persistControllerInstances);
- }
- return persistControllerInstances;
+ return createAdd(beanPersistControllerInstances, beanPersistControllerCandidates);
}
public List getBeanPostLoaders() {
- // add class registered BeanPostLoad to the already created instances
- for (Class> cls : beanPostLoadList) {
- createAdd(cls, beanPostLoadInstances);
- }
- return beanPostLoadInstances;
+ return createAdd(beanPostLoadInstances, beanPostLoadCandidates);
+ }
+
+ public List getBeanPostConstructoListeners() {
+ return createAdd(beanPostConstructListenerInstances, beanPostConstructListenerCandidates);
}
- public List getIdGenerators() {
- for (Class> cls : idGeneratorList) {
- createAdd(cls, idGeneratorInstances);
- }
- return idGeneratorInstances;
+ public List getBeanFindControllers() {
+ return createAdd(beanFindControllerInstances, beanFindControllerCandidates);
}
-
+
+ public List getBeanPersistListeners() {
+ return createAdd(beanPersistListenerInstances, beanPersistListenerCandidates);
+ }
+
+ public List getBeanQueryAdapters() {
+ return createAdd(beanQueryAdapterInstances, beanQueryAdapterCandidates);
+ }
+
/**
* Return the list of Embeddable classes.
*/
@@ -351,21 +336,21 @@ public class BootupClasses implements ClassFilter {
/**
* Return the list of ScalarTypes found.
*/
- public List> getScalarTypes() {
+ public List>> getScalarTypes() {
return scalarTypeList;
}
/**
* Return the list of ScalarConverters found.
*/
- public List> getScalarConverters() {
+ public List>> getScalarConverters() {
return scalarConverterList;
}
/**
* Return the list of ScalarConverters found.
*/
- public List> getCompoundTypes() {
+ public List>> getCompoundTypes() {
return compoundTypeList;
}
@@ -391,6 +376,7 @@ public class BootupClasses implements ClassFilter {
* This includes ScalarType, BeanController, BeanFinder and BeanListener.
*
*/
+ @SuppressWarnings("unchecked")
private boolean isInterestingInterface(Class> cls) {
if (Modifier.isAbstract(cls.getModifiers())) {
@@ -400,77 +386,87 @@ public class BootupClasses implements ClassFilter {
}
boolean interesting = false;
- if (IdGenerator.class.isAssignableFrom(cls)) {
- idGeneratorList.add(cls);
- interesting = true;
- }
-
- if (BeanPersistController.class.isAssignableFrom(cls)) {
- beanControllerList.add(cls);
- interesting = true;
- }
-
- if (BeanPostLoad.class.isAssignableFrom(cls)) {
- beanPostLoadList.add(cls);
- interesting = true;
- }
-
+ // Types
if (ScalarType.class.isAssignableFrom(cls)) {
- scalarTypeList.add(cls);
- interesting = true;
+ scalarTypeList.add((Class extends ScalarType>>) cls);
+ interesting = true;
}
if (ScalarTypeConverter.class.isAssignableFrom(cls)) {
- scalarConverterList.add(cls);
+ scalarConverterList.add((Class extends ScalarTypeConverter, ?>>) cls);
interesting = true;
}
if (CompoundType.class.isAssignableFrom(cls)) {
- compoundTypeList.add(cls);
+ compoundTypeList.add((Class extends CompoundType>>) cls);
+ interesting = true;
+ }
+
+ if (IdGenerator.class.isAssignableFrom(cls)) {
+ idGeneratorCandidates.add((Class extends IdGenerator>) cls);
+ interesting = true;
+ }
+
+ // "Candidates"
+ if (BeanPersistController.class.isAssignableFrom(cls)) {
+ beanPersistControllerCandidates.add((Class extends BeanPersistController>) cls);
+ interesting = true;
+ }
+
+ if (BeanPostLoad.class.isAssignableFrom(cls)) {
+ beanPostLoadCandidates.add((Class extends BeanPostLoad>) cls);
+ interesting = true;
+ }
+
+ if (BeanPostConstructListener.class.isAssignableFrom(cls)) {
+ beanPostConstructListenerCandidates.add((Class extends BeanPostConstructListener>) cls);
interesting = true;
}
if (BeanFindController.class.isAssignableFrom(cls)) {
- beanFindControllerList.add(cls);
+ beanFindControllerCandidates.add((Class extends BeanFindController>) cls);
interesting = true;
- }
+ }
if (BeanPersistListener.class.isAssignableFrom(cls)) {
- beanListenerList.add(cls);
+ beanPersistListenerCandidates.add((Class extends BeanPersistListener>) cls);
interesting = true;
}
if (BeanQueryAdapter.class.isAssignableFrom(cls)) {
- beanQueryAdapterList.add(cls);
+ beanQueryAdapterCandidates.add((Class extends BeanQueryAdapter>) cls);
interesting = true;
}
if (ServerConfigStartup.class.isAssignableFrom(cls)) {
- serverConfigStartupList.add(cls);
+ serverConfigStartupCandidates.add((Class extends ServerConfigStartup>) cls);
interesting = true;
}
+ // single instances
+ // TODO: What should happen, if there is already an other
+ // changeLogListener assigned? (Last wins? / Exception?)
if (ChangeLogListener.class.isAssignableFrom(cls)) {
- changeLogListenerClass = cls;
+ changeLogListenerClass = (Class extends ChangeLogListener>) cls;
interesting = true;
}
if (ChangeLogRegister.class.isAssignableFrom(cls)) {
- changeLogRegisterClass = cls;
+ changeLogRegisterClass = (Class extends ChangeLogRegister>) cls;
interesting = true;
}
if (ChangeLogPrepare.class.isAssignableFrom(cls)) {
- changeLogPrepareClass = cls;
+ changeLogPrepareClass = (Class extends ChangeLogPrepare>) cls;
interesting = true;
}
if (ReadAuditPrepare.class.isAssignableFrom(cls)) {
- readAuditPrepareClass = cls;
+ readAuditPrepareClass = (Class extends ReadAuditPrepare>) cls;
interesting = true;
}
if (ReadAuditLogger.class.isAssignableFrom(cls)) {
- readAuditLoggerClass = cls;
+ readAuditLoggerClass = (Class extends ReadAuditLogger>) cls;
interesting = true;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
index fca0972de..b04cd4218 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -20,6 +20,7 @@ import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
+import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.changelog.BeanChange;
@@ -242,6 +243,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
private volatile BeanPersistController persistController;
private final BeanPostLoad beanPostLoad;
+ private final BeanPostConstructListener beanPostConstructListener;
/**
* Listens for post commit insert update and delete events.
@@ -424,6 +426,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
this.beanFinder = deploy.getBeanFinder();
this.persistController = deploy.getPersistController();
this.persistListener = deploy.getPersistListener();
+ this.beanPostConstructListener = deploy.getPostConstructListener();
this.beanPostLoad = deploy.getPostLoad();
this.queryAdapter = deploy.getQueryAdapter();
this.changeLogFilter = deploy.getChangeLogFilter();
@@ -1558,17 +1561,26 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
@Override
@SuppressWarnings("unchecked")
public T createBean() {
- return (T) createEntityBean();
+ return (T) createEntityBean(true);
}
/**
* Creates a new EntityBean.
+ * The parameter isNew controls either this is a new bean (then
+ * {@link BeanPostConstructListener#postCreate(Object)} will be invoked) or
+ * a reference (then {@link BeanPostLoad#postLoad(Object)} will be invoked
+ * on first access (lazy load) or immediately (eager load)
*/
@SuppressWarnings("unchecked")
- public EntityBean createEntityBean() {
+ public EntityBean createEntityBean(boolean isNew) {
try {
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
+ if (beanPostConstructListener != null) {
+ beanPostConstructListener.autowire(bean); // calls all registered listeners
+ beanPostConstructListener.postConstruct(bean); // calls first the @PostConstruct method and then the listeners
+ }
+
if (unloadProperties.length > 0) {
// 'unload' any properties initialised in the default constructor
EntityBeanIntercept ebi = bean._ebean_getIntercept();
@@ -1576,12 +1588,23 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
ebi.setPropertyUnloaded(unloadProperties[i]);
}
}
+ if (beanPostConstructListener != null && isNew) {
+ beanPostConstructListener.postCreate(bean);
+ // if bean is not new, postLoad will be executed later in the bean's lifecycle
+ }
return bean;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
+
+ /**
+ * Creates a new entitybean without invoking {@link BeanPostConstructListener#postCreate(Object)}
+ */
+ public EntityBean createEntityBean() {
+ return createEntityBean(false);
+ }
/**
* Create a reference bean based on the id.
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 864c34586..37556b202 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java
@@ -107,6 +107,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final PostLoadManager postLoadManager;
+ private final PostConstructManager postConstructManager;
+
private final BeanFinderManager beanFinderManager;
private final PersistListenerManager persistListenerManager;
@@ -219,6 +221,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.postLoadManager = new PostLoadManager(bootupClasses);
+ this.postConstructManager = new PostConstructManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
this.beanFinderManager = new BeanFinderManager(bootupClasses);
@@ -624,10 +627,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
int qa = beanQueryAdapterManager.getRegisterCount();
int cc = persistControllerManager.getRegisterCount();
int pl = postLoadManager.getRegisterCount();
+ int pc = postConstructManager.getRegisterCount();
int lc = persistListenerManager.getRegisterCount();
int fc = beanFinderManager.getRegisterCount();
- logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "] BeanPostLoaders[" + pl + "]");
+ logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "] BeanPostLoaders[" + pl + "] BeanPostConstructors[" + pc + "]");
}
private void logStatus() {
@@ -1143,6 +1147,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
persistControllerManager.addPersistControllers(descriptor);
postLoadManager.addPostLoad(descriptor);
+ postConstructManager.addPostConstructListeners(descriptor);
persistListenerManager.addPersistListeners(descriptor);
beanQueryAdapterManager.addQueryAdapter(descriptor);
beanFinderManager.addFindControllers(descriptor);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java
index 70add7faf..19e782aab 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java
@@ -5,6 +5,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+import javax.annotation.PostConstruct;
import javax.persistence.PersistenceException;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
@@ -16,6 +17,7 @@ import javax.persistence.PreUpdate;
import com.avaje.ebean.event.BeanPersistAdapter;
import com.avaje.ebean.event.BeanPersistRequest;
+import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
@@ -50,6 +52,10 @@ public class BeanLifecycleAdapterFactory {
// has postLoad methods
deployDesc.addPostLoad(new PostLoadAdapter(methodHolder.postLoads));
}
+ if (!methodHolder.postConstructs.isEmpty()) {
+ // has postConstruct methods
+ deployDesc.addPostConstructListener(new PostConstructAdapter(methodHolder.postConstructs));
+ }
}
/**
@@ -65,6 +71,7 @@ public class BeanLifecycleAdapterFactory {
private final List preDeletes = new ArrayList();
private final List postDeletes = new ArrayList();
private final List postLoads = new ArrayList();
+ private final List postConstructs = new ArrayList();
/**
* Has one of the pre or post insert update delete annotated methods.
@@ -107,6 +114,9 @@ public class BeanLifecycleAdapterFactory {
if (method.isAnnotationPresent(PostLoad.class)) {
postLoads.add(method);
}
+ if (method.isAnnotationPresent(PostConstruct.class)) {
+ postConstructs.add(method);
+ }
}
}
@@ -241,4 +251,49 @@ public class BeanLifecycleAdapterFactory {
}
}
}
+
+ /**
+ * PostConstructAdapter using reflection to invoke lifecycle methods.
+ */
+ private static class PostConstructAdapter implements BeanPostConstructListener {
+
+ private final Method[] postConstructMethods;
+
+ private PostConstructAdapter(List postConstructMethods) {
+ this.postConstructMethods = toArray(postConstructMethods);
+ }
+
+ @Override
+ public boolean isRegisterFor(Class> cls) {
+ // Not used
+ return false;
+ }
+
+ private void invoke(Method method, Object bean) {
+ try {
+ method.invoke(bean);
+ } catch (InvocationTargetException e) {
+ throw new PersistenceException("Error invoking lifecycle method", e);
+ } catch (IllegalAccessException e) {
+ throw new PersistenceException("Error invoking lifecycle method", e);
+ }
+ }
+
+ @Override
+ public void postConstruct(Object bean) {
+ for (int i = 0; i < postConstructMethods.length; i++) {
+ invoke(postConstructMethods[i], bean);
+ }
+ }
+
+ @Override
+ public void autowire(Object bean) {
+ // autowire is done by global PostConstructListener only
+ }
+
+ @Override
+ public void postCreate(Object bean) {
+ // postCreate is done by global PostConstructListener only
+ }
+ }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java
index 1ad5b2a82..a5a211209 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java
@@ -16,7 +16,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
private final BeanPersistListener[] chain;
/**
- * Construct adding 2 BeanPersistController's.
+ * Construct adding 2 BeanPersistListener's.
*/
public ChainedBeanPersistListener(BeanPersistListener c1, BeanPersistListener c2) {
this(addList(c1, c2));
@@ -46,7 +46,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
/**
- * Construct given the list of BeanPersistController's.
+ * Construct given the list of BeanPersistListener's.
*/
public ChainedBeanPersistListener(List list) {
this.list = list;
@@ -54,7 +54,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
/**
- * Register a new BeanPersistController and return the resulting chain.
+ * Register a new BeanPersistListener and return the resulting chain.
*/
public ChainedBeanPersistListener register(BeanPersistListener c) {
if (list.contains(c)){
@@ -69,7 +69,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
/**
- * De-register a BeanPersistController and return the resulting chain.
+ * De-register a BeanPersistListener and return the resulting chain.
*/
public ChainedBeanPersistListener deregister(BeanPersistListener c) {
if (!list.contains(c)){
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostConstructListener.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostConstructListener.java
new file mode 100644
index 000000000..a4f754300
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostConstructListener.java
@@ -0,0 +1,91 @@
+package com.avaje.ebeaninternal.server.deploy;
+
+import com.avaje.ebean.event.BeanPostConstructListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles multiple BeanPostLoad's for a given entity type.
+ */
+public class ChainedBeanPostConstructListener implements BeanPostConstructListener {
+
+ private final List list;
+
+ private final BeanPostConstructListener[] chain;
+
+ /**
+ * Construct given the list of BeanPostCreate's.
+ */
+ public ChainedBeanPostConstructListener(List list) {
+ this.list = list;
+ this.chain = list.toArray(new BeanPostConstructListener[list.size()]);
+ }
+
+ /**
+ * Register a new BeanPostCreate and return the resulting chain.
+ */
+ public ChainedBeanPostConstructListener register(BeanPostConstructListener c) {
+ if (list.contains(c)){
+ return this;
+ } else {
+ List newList = new ArrayList();
+ newList.addAll(list);
+ newList.add(c);
+
+ return new ChainedBeanPostConstructListener(newList);
+ }
+ }
+
+ /**
+ * De-register a BeanPostCreate and return the resulting chain.
+ */
+ public BeanPostConstructListener deregister(BeanPostConstructListener c) {
+ if (!list.contains(c)){
+ return this;
+ } else {
+ ArrayList newList = new ArrayList();
+ newList.addAll(list);
+ newList.remove(c);
+
+ return new ChainedBeanPostConstructListener(newList);
+ }
+ }
+
+ /**
+ * Return the size of the chain.
+ */
+ protected int size() {
+ return chain.length;
+ }
+
+ @Override
+ public boolean isRegisterFor(Class> cls) {
+ // never called
+ return false;
+ }
+
+ /**
+ * Fire postLoad on all registered BeanPostCreate implementations.
+ */
+ @Override
+ public void postConstruct(Object bean) {
+ for (int i = 0; i < chain.length; i++) {
+ chain[i].postConstruct(bean);
+ }
+ }
+
+ @Override
+ public void autowire(Object bean) {
+ for (int i = 0; i < chain.length; i++) {
+ chain[i].autowire(bean);
+ }
+ }
+
+ @Override
+ public void postCreate(Object bean) {
+ for (int i = 0; i < chain.length; i++) {
+ chain[i].postCreate(bean);
+ }
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java
index 4fdbea86e..ab31a33a7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java
@@ -15,7 +15,7 @@ public class ChainedBeanPostLoad implements BeanPostLoad {
private final BeanPostLoad[] chain;
/**
- * Construct given the list of BeanPersistController's.
+ * Construct given the list of BeanPostLoad's.
*/
public ChainedBeanPostLoad(List list) {
this.list = list;
@@ -23,7 +23,7 @@ public class ChainedBeanPostLoad implements BeanPostLoad {
}
/**
- * Register a new BeanPersistController and return the resulting chain.
+ * Register a new BeanPostLoad and return the resulting chain.
*/
public ChainedBeanPostLoad register(BeanPostLoad c) {
if (list.contains(c)){
@@ -38,7 +38,7 @@ public class ChainedBeanPostLoad implements BeanPostLoad {
}
/**
- * De-register a BeanPersistController and return the resulting chain.
+ * De-register a BeanPostLoad and return the resulting chain.
*/
public ChainedBeanPostLoad deregister(BeanPostLoad c) {
if (!list.contains(c)){
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/PostConstructManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/PostConstructManager.java
new file mode 100644
index 000000000..8e7a84041
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/PostConstructManager.java
@@ -0,0 +1,42 @@
+package com.avaje.ebeaninternal.server.deploy;
+
+import com.avaje.ebean.event.BeanPostConstructListener;
+import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
+import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Default implementation for creating BeanControllers.
+ */
+public class PostConstructManager {
+
+ private static final Logger logger = LoggerFactory.getLogger(PostConstructManager.class);
+
+ private final List list;
+
+ public PostConstructManager(BootupClasses bootupClasses) {
+ this.list = bootupClasses.getBeanPostConstructoListeners();
+ }
+
+ public int getRegisterCount() {
+ return list.size();
+ }
+
+ /**
+ * Register BeanPostLoad listeners for a given entity type.
+ */
+ public void addPostConstructListeners(DeployBeanDescriptor> deployDesc) {
+
+ for (int i = 0; i < list.size(); i++) {
+ BeanPostConstructListener c = list.get(i);
+ if (c.isRegisterFor(deployDesc.getBeanType())) {
+ logger.debug("BeanPostLoad on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
+ deployDesc.addPostConstructListener(c);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
index c04891764..c382068c4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
@@ -11,6 +11,7 @@ import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
+import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.changelog.ChangeLogFilter;
@@ -22,6 +23,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener;
+import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostConstructListener;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
@@ -155,6 +157,7 @@ public class DeployBeanDescriptor {
private final List persistListeners = new ArrayList();
private final List queryAdapters = new ArrayList();
private final List postLoaders = new ArrayList();
+ private final List postConstructListeners = new ArrayList();
private CacheOptions cacheOptions = CacheOptions.NO_CACHING;
@@ -175,8 +178,6 @@ public class DeployBeanDescriptor {
private String name;
- private boolean processedRawSqlExtend;
-
private ChangeLogFilter changeLogFilter;
private String dbComment;
@@ -533,6 +534,19 @@ public class DeployBeanDescriptor {
}
}
+ /**
+ * Return the BeanPostCreate(could be a chain of them, 1 or null).
+ */
+ public BeanPostConstructListener getPostConstructListener() {
+ if (postConstructListeners.isEmpty()) {
+ return null;
+ } else if (postConstructListeners.size() == 1) {
+ return postConstructListeners.get(0);
+ } else {
+ return new ChainedBeanPostConstructListener(postConstructListeners);
+ }
+ }
+
public void addPersistController(BeanPersistController controller) {
persistControllers.add(controller);
}
@@ -549,6 +563,10 @@ public class DeployBeanDescriptor {
postLoaders.add(postLoad);
}
+ public void addPostConstructListener(BeanPostConstructListener postConstructListener) {
+ postConstructListeners.add(postConstructListener);
+ }
+
public String getDraftTable() {
return draftTable;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
index 1bdf25101..b260d9cb7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
@@ -688,22 +688,22 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
customScalarTypes.add(longToTimestamp);
- List> foundTypes = bootupClasses.getScalarTypes();
+ List>> foundTypes = bootupClasses.getScalarTypes();
for (int i = 0; i < foundTypes.size(); i++) {
- Class> cls = foundTypes.get(i);
+ Class extends ScalarType>> cls = foundTypes.get(i);
try {
ScalarType> scalarType;
if (objectMapper == null) {
- scalarType = (ScalarType>) cls.newInstance();
+ scalarType = cls.newInstance();
} else {
try {
// first try objectMapper constructor
- Constructor> constructor = cls.getConstructor(ObjectMapper.class);
- scalarType = (ScalarType>) constructor.newInstance((ObjectMapper) objectMapper);
+ Constructor extends ScalarType>> constructor = cls.getConstructor(ObjectMapper.class);
+ scalarType = constructor.newInstance((ObjectMapper) objectMapper);
} catch (NoSuchMethodException e) {
- scalarType = (ScalarType>) cls.newInstance();
+ scalarType = cls.newInstance();
}
}
@@ -730,7 +730,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
@SuppressWarnings({"unchecked", "rawtypes"})
protected void initialiseScalarConverters(BootupClasses bootupClasses) {
- List> foundTypes = bootupClasses.getScalarConverters();
+ List>> foundTypes = bootupClasses.getScalarConverters();
for (int i = 0; i < foundTypes.size(); i++) {
Class> cls = foundTypes.get(i);
@@ -766,7 +766,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
protected void initialiseCompoundTypes(BootupClasses bootupClasses) {
- List> compoundTypes = bootupClasses.getCompoundTypes();
+ List>> compoundTypes = bootupClasses.getCompoundTypes();
for (int j = 0; j < compoundTypes.size(); j++) {
Class> type = compoundTypes.get(j);
diff --git a/src/test/java/com/avaje/tests/history/TestHistoryInsert.java b/src/test/java/com/avaje/tests/history/TestHistoryInsert.java
index 3ffa0b8b7..c3203fc5d 100644
--- a/src/test/java/com/avaje/tests/history/TestHistoryInsert.java
+++ b/src/test/java/com/avaje/tests/history/TestHistoryInsert.java
@@ -54,6 +54,7 @@ public class TestHistoryInsert extends BaseTestCase {
user.setName("Jim v3");
user.setEmail("three@email.com");
+ Thread.sleep(10); // otherwise the timestamp of "whenModified" may not change
Ebean.save(user);
history = fetchHistory(user);
diff --git a/src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java b/src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java
index 6d4f954fc..aab8519e7 100644
--- a/src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java
+++ b/src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java
@@ -2,6 +2,8 @@ package com.avaje.tests.lifecycle;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
+import com.avaje.ebeaninternal.api.SpiEbeanServer;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.tests.model.basic.EBasicWithLifecycle;
import org.junit.Test;
@@ -101,4 +103,60 @@ public class TestLifecycleAnnotatedBean extends BaseTestCase {
assertThat(bean.getBuffer()).contains("postRemove1");
assertThat(bean.getBuffer()).contains("postRemove2");
}
+ @Test
+ public void shouldExecutePostConstructMethodsWhenFindingBean() {
+
+ EBasicWithLifecycle bean = new EBasicWithLifecycle();
+ bean.setName("PostConstruct");
+
+ Ebean.save(bean);
+
+ EBasicWithLifecycle loaded = Ebean.find(EBasicWithLifecycle.class, bean.getId());
+ assertThat(loaded.getBuffer()).contains("postConstruct1");
+ assertThat(loaded.getBuffer()).contains("postConstruct2");
+ // assert also that postLoad was executed
+ assertThat(loaded.getBuffer()).contains("postLoad1");
+ assertThat(loaded.getBuffer()).contains("postLoad2");
+ }
+
+ @Test
+ public void shouldExecutePostConstructMethodsWhenInstantiated() {
+ EBasicWithLifecycle bean = Ebean.getDefaultServer().createEntityBean(EBasicWithLifecycle.class);
+ bean.setName("PostConstruct");
+
+
+ assertThat(bean.getBuffer()).contains("postConstruct1");
+ assertThat(bean.getBuffer()).contains("postConstruct2");
+ // assert also that postLoad is not executed now
+ assertThat(bean.getBuffer()).doesNotContain("postLoad1");
+ assertThat(bean.getBuffer()).doesNotContain("postLoad2");
+ }
+
+
+
+ @Test
+ public void testLazyLoadBehaviour() {
+
+ EBasicWithLifecycle bean = new EBasicWithLifecycle();
+ bean.setName("LazyLoad");
+
+ Ebean.save(bean);
+
+ BeanDescriptor desc = ((SpiEbeanServer)server()).getBeanDescriptor(EBasicWithLifecycle.class);
+ EBasicWithLifecycle loaded = desc.createReference(false, false, bean.getId(), null);
+
+ // Here you see the big difference.
+ // @PostLoad is executed always, also on lazy loaded beans
+ assertThat(loaded.getBuffer()).contains("postConstruct1");
+ assertThat(loaded.getBuffer()).contains("postConstruct2");
+
+ // assert also that postLoad is not yet executed
+ assertThat(loaded.getBuffer()).doesNotContain("postLoad1");
+ assertThat(loaded.getBuffer()).doesNotContain("postLoad2");
+
+ // now read name -> will load the bean
+ assertThat(loaded.getName()).isEqualTo("LazyLoad");
+ assertThat(loaded.getBuffer()).contains("postLoad1");
+ assertThat(loaded.getBuffer()).contains("postLoad2");
+ }
}
diff --git a/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java b/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java
index f3b79f704..f3fe13160 100644
--- a/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java
+++ b/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java
@@ -1,5 +1,6 @@
package com.avaje.tests.model.basic;
+import javax.annotation.PostConstruct;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PostLoad;
@@ -96,6 +97,16 @@ public class EBasicWithLifecycle {
buffer.append("postLoad2");
}
+ @PostConstruct
+ public void postConstruct1() {
+ buffer.append("postConstruct1");
+ }
+
+ @PostConstruct
+ public void postConstruct2() {
+ buffer.append("postConstruct2");
+ }
+
public Long getId() {
return id;
}