From 169eba429677b29981bc3a80991e38116cb50fd8 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 5 Oct 2016 21:39:44 +0200 Subject: [PATCH 1/5] No effective code change - fixed commets --- src/main/java/com/avaje/ebean/EbeanServer.java | 5 +++-- .../server/deploy/ChainedBeanPersistListener.java | 8 ++++---- .../ebeaninternal/server/deploy/ChainedBeanPostLoad.java | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index d4f72b1a5..a2bb1f4b4 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -167,8 +167,9 @@ public interface EbeanServer { /** * Create a new instance of T that is an EntityBean. *

- * Generally not expected to be useful (now dynamic subclassing support was removed in - * favour of always using enhancement). + * Useful if you use BeanPostConstructListeners or @PostConstruct Annotations. + * In this case you should not use "new Bean...()". Making all bean construtors protected + * could be a good idea here. *

*/ T createEntityBean(Class type); 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/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)){ From 3ee0b0224edf11137eca0086d67a0f4b5a0bc1d8 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 5 Oct 2016 21:48:11 +0200 Subject: [PATCH 2/5] ENH: Support for javax.annotation.PostConstruct. BeanPostConstructListener will be informed when an entity-bean is constructed/created to perform dependency-injection (e.g. Spring-Autowire) --- .../com/avaje/ebean/config/ServerConfig.java | 25 +++++ .../event/BeanPostConstructListener.java | 41 +++++++++ .../server/core/DefaultContainer.java | 1 + .../server/core/DefaultServer.java | 2 +- .../server/deploy/BeanDescriptor.java | 27 +++++- .../server/deploy/BeanDescriptorManager.java | 7 +- .../deploy/BeanLifecycleAdapterFactory.java | 55 +++++++++++ .../ChainedBeanPostConstructListener.java | 91 +++++++++++++++++++ .../server/deploy/PostConstructManager.java | 42 +++++++++ .../deploy/meta/DeployBeanDescriptor.java | 20 ++++ 10 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/avaje/ebean/event/BeanPostConstructListener.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostConstructListener.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/deploy/PostConstructManager.java diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 70cf6dbcf..f66c56111 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -11,6 +11,7 @@ import com.avaje.ebean.config.dbplatform.DbType; 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.BulkTableEventListener; @@ -329,6 +330,7 @@ public class ServerConfig { private List findControllers = new ArrayList(); private List persistControllers = new ArrayList(); private List postLoaders = new ArrayList(); + private List postConstructListeners = new ArrayList(); private List persistListeners = new ArrayList(); private List queryAdapters = new ArrayList(); private List bulkTableEventListeners = new ArrayList(); @@ -2055,6 +2057,16 @@ public class ServerConfig { postLoaders.add(postLoad); } + /** + * Register a BeanPostConstructListener instance. + *

+ * Note alternatively you can use {@link #setPostConstructListeners(List)} to set + * all the BeanPostConstructListener instances. + *

+ */ + public void add(BeanPostConstructListener listener) { + postConstructListeners.add(listener); + } /** * Return the list of BeanFindController instances. */ @@ -2083,6 +2095,19 @@ public class ServerConfig { this.postLoaders = postLoaders; } + /** + * Return the list of BeanPostLoader instances. + */ + public List getPostConstructListeners() { + return postConstructListeners; + } + + /** + * Set the list of BeanPostLoader instances. + */ + public void setPostConstructListeners(List listeners) { + this.postConstructListeners = listeners; + } /** * Return the BeanPersistController instances. */ diff --git a/src/main/java/com/avaje/ebean/event/BeanPostConstructListener.java b/src/main/java/com/avaje/ebean/event/BeanPostConstructListener.java new file mode 100644 index 000000000..1893b480e --- /dev/null +++ b/src/main/java/com/avaje/ebean/event/BeanPostConstructListener.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.event; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Fired after a bean is constructed, but not yet loaded from database. + *

+ * Note: You MUST NOT set any default values, as in a following step, + * properties will get unload. Use {@link BeanPostLoad} instead. + * + * it's intended to do some dependency-injection here. + * If you plan to use this feature you should use {@link EbeanServer#createEntityBean(Class)} + * to create new beans. + *

+ */ +public interface BeanPostConstructListener { + + /** + * Return true if this BeanPostConstructListener instance should be registered + * for post construct on this entity type. + */ + boolean isRegisterFor(Class cls); + + /** + * Called immediately after construction. Perform DI here. + */ + void autowire(Object bean); + + /** + * Called after every @PostConstruct annotated method of the bean is executed + */ + void postConstruct(Object bean); + + /** + * Called after {@link EbeanServer#createEntityBean(Class)}. Only for new beans. + * intended to set default values here. + */ + void postCreate(Object bean); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java index 0f25e5cc7..009513583 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -194,6 +194,7 @@ public class DefaultContainer implements SpiContainer { bootup.addIdGenerators(serverConfig.getIdGenerators()); bootup.addPersistControllers(serverConfig.getPersistControllers()); bootup.addPostLoaders(serverConfig.getPostLoaders()); + bootup.addPostConstructListeners(serverConfig.getPostConstructListeners()); bootup.addFindControllers(serverConfig.getFindControllers()); bootup.addPersistListeners(serverConfig.getPersistListeners()); bootup.addQueryAdapters(serverConfig.getQueryAdapters()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index a87e07960..99aab043f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -523,7 +523,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @SuppressWarnings("unchecked") public T createEntityBean(Class type) { BeanDescriptor desc = getBeanDescriptor(type); - return (T) desc.createEntityBean(); + return (T) desc.createEntityBean(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/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/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..c29a7b6e3 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; @@ -533,6 +536,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 +565,10 @@ public class DeployBeanDescriptor { postLoaders.add(postLoad); } + public void addPostConstructListener(BeanPostConstructListener postConstructListener) { + postConstructListeners.add(postConstructListener); + } + public String getDraftTable() { return draftTable; } From fe7e929a99d796b9ebdc0a41f2617b38e255c034 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 5 Oct 2016 21:49:18 +0200 Subject: [PATCH 3/5] Refactor: Removed duplicate code from BootupClasses - use generics. --- .../server/core/bootup/BootupClasses.java | 342 +++++++++--------- .../deploy/meta/DeployBeanDescriptor.java | 2 - .../server/type/DefaultTypeManager.java | 16 +- 3 files changed, 177 insertions(+), 183 deletions(-) diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java b/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java index d846536d8..1722d0962 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/bootup/BootupClasses.java @@ -7,6 +7,7 @@ import com.avaje.ebean.config.ServerConfig; 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.ServerConfigStartup; @@ -41,38 +42,59 @@ public class BootupClasses implements ClassFilter { private final List> entityList = new ArrayList>(); - private final List> scalarTypeList = new ArrayList>(); + private final List>> scalarTypeList + = new ArrayList>>(); - private final List> scalarConverterList = new ArrayList>(); + private final List>> scalarConverterList + = new ArrayList>>(); - private final List> compoundTypeList = new ArrayList>(); + private final List>> compoundTypeList + = new ArrayList>>(); - private final List> idGeneratorList = new ArrayList>(); + + // The following objects are instantiiated on first request + // there is always a candidate list, that holds the class and an + // instance list, that holds the instance. Once a class is instantiiated + // (or added) it will get removed from the candidate list + private final List> idGeneratorCandidates + = new ArrayList>(); + + private final List> beanPersistControllerCandidates + = new ArrayList>(); + + private final List> beanPostLoadCandidates + = new ArrayList>(); - private final List> beanControllerList = new ArrayList>(); - - private final List> beanPostLoadList = new ArrayList>(); - - private final List> beanFindControllerList = new ArrayList>(); - private final List> beanQueryAdapterList = new ArrayList>(); - - private final List> beanListenerList = new ArrayList>(); - - private final List> serverConfigStartupList = new ArrayList>(); - private final List serverConfigStartupInstances = new ArrayList(); + private final List> beanPostConstructListenerCandidates + = new ArrayList>(); + + private final List> beanFindControllerCandidates + = new ArrayList>(); + + private final List> beanPersistListenerCandidates + = new ArrayList>(); + + private final List> beanQueryAdapterCandidates + = new ArrayList>(); + + private final List> serverConfigStartupCandidates + = new ArrayList>(); private final List idGeneratorInstances = new ArrayList(); - private final List findControllerInstances = new ArrayList(); - private final List persistControllerInstances = new ArrayList(); + private final List beanPersistControllerInstances = new ArrayList(); private final List beanPostLoadInstances = new ArrayList(); - private final List persistListenerInstances = new ArrayList(); - private final List queryAdapterInstances = new ArrayList(); - - private Class changeLogPrepareClass; - private Class changeLogListenerClass; - private Class changeLogRegisterClass; - private Class readAuditPrepareClass; - private Class readAuditLoggerClass; + private final List beanPostConstructListenerInstances = new ArrayList(); + private final List beanFindControllerInstances = new ArrayList(); + private final List beanPersistListenerInstances = new ArrayList(); + private final List beanQueryAdapterInstances = new ArrayList(); + private final List serverConfigStartupInstances = new ArrayList(); + + // single objects + private Class changeLogPrepareClass; + private Class changeLogListenerClass; + private Class changeLogRegisterClass; + private Class readAuditPrepareClass; + private Class readAuditLoggerClass; private ChangeLogPrepare changeLogPrepare; private ChangeLogListener changeLogListener; @@ -96,7 +118,7 @@ public class BootupClasses implements ClassFilter { */ public void runServerConfigStartup(ServerConfig serverConfig) { - for (Class cls : serverConfigStartupList) { + for (Class cls : serverConfigStartupCandidates) { try { ServerConfigStartup newInstance = (ServerConfigStartup) cls.newInstance(); newInstance.onStart(serverConfig); @@ -115,114 +137,90 @@ public class BootupClasses implements ClassFilter { } } + /** + * Adds the list toAdd to instances and removes any pending + * candiate, to prevent duplicate instantiiation. + */ + private void add(List toAdd, List instances, List> candidates) { + if (toAdd != null) { + for (T obj : toAdd) { + instances.add(obj); + // don't automatically instantiate + candidates.remove(obj.getClass()); + } + } + } + /** * Add IdGenerator instances (registered explicitly with the ServerConfig). */ public void addIdGenerators(List idGenerators) { - if (idGenerators != null) { - for (IdGenerator c : idGenerators) { - this.idGeneratorInstances.add(c); - // don't automatically instantiate - this.idGeneratorList.remove(c.getClass()); - } - } - } - - public void addQueryAdapters(List queryAdapterInstances) { - if (queryAdapterInstances != null) { - for (BeanQueryAdapter a : queryAdapterInstances) { - this.queryAdapterInstances.add(a); - // don't automatically instantiate - this.beanQueryAdapterList.remove(a.getClass()); - } - } + add(idGenerators, idGeneratorInstances, idGeneratorCandidates); } /** * Add BeanPersistController instances. */ - public void addPersistControllers(List beanControllerInstances) { - if (beanControllerInstances != null) { - for (BeanPersistController c : beanControllerInstances) { - this.persistControllerInstances.add(c); - // don't automatically instantiate - this.beanControllerList.remove(c.getClass()); - } - } + public void addPersistControllers(List beanControllers) { + add(beanControllers, beanPersistControllerInstances, beanPersistControllerCandidates); } /** * Add BeanPostLoad instances. */ - public void addPostLoaders(List postLoadInstances) { - if (postLoadInstances != null) { - for (BeanPostLoad c : postLoadInstances) { - this.beanPostLoadInstances.add(c); - // don't automatically instantiate - this.beanPostLoadList.remove(c.getClass()); - } - } + public void addPostLoaders(List postLoaders) { + add(postLoaders, beanPostLoadInstances, beanPostLoadCandidates); } - + /** + * Add BeanPostConstructListener instances. + */ + public void addPostConstructListeners(List postConstructListener) { + add(postConstructListener, beanPostConstructListenerInstances, beanPostConstructListenerCandidates); + } + /** * Add BeanFindController instances. */ public void addFindControllers(List findControllers) { - if (findControllers != null) { - for (BeanFindController c : findControllers) { - this.findControllerInstances.add(c); - // don't automatically instantiate - this.beanFindControllerList.remove(c.getClass()); - } - } + add(findControllers, beanFindControllerInstances, beanFindControllerCandidates); } public void addPersistListeners(List listenerInstances) { - if (listenerInstances != null) { - for (BeanPersistListener l : listenerInstances) { - this.persistListenerInstances.add(l); - // don't automatically instantiate - this.beanListenerList.remove(l.getClass()); - } - } + add(listenerInstances, beanPersistListenerInstances, beanPersistListenerCandidates); } + public void addQueryAdapters(List queryAdapters) { + add(queryAdapters, beanQueryAdapterInstances, beanQueryAdapterCandidates); + } + public void addServerConfigStartup(List 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 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>) cls); + interesting = true; } if (ScalarTypeConverter.class.isAssignableFrom(cls)) { - scalarConverterList.add(cls); + scalarConverterList.add((Class>) cls); interesting = true; } if (CompoundType.class.isAssignableFrom(cls)) { - compoundTypeList.add(cls); + compoundTypeList.add((Class>) cls); + interesting = true; + } + + if (IdGenerator.class.isAssignableFrom(cls)) { + idGeneratorCandidates.add((Class) cls); + interesting = true; + } + + // "Candidates" + if (BeanPersistController.class.isAssignableFrom(cls)) { + beanPersistControllerCandidates.add((Class) cls); + interesting = true; + } + + if (BeanPostLoad.class.isAssignableFrom(cls)) { + beanPostLoadCandidates.add((Class) cls); + interesting = true; + } + + if (BeanPostConstructListener.class.isAssignableFrom(cls)) { + beanPostConstructListenerCandidates.add((Class) cls); interesting = true; } if (BeanFindController.class.isAssignableFrom(cls)) { - beanFindControllerList.add(cls); + beanFindControllerCandidates.add((Class) cls); interesting = true; - } + } if (BeanPersistListener.class.isAssignableFrom(cls)) { - beanListenerList.add(cls); + beanPersistListenerCandidates.add((Class) cls); interesting = true; } if (BeanQueryAdapter.class.isAssignableFrom(cls)) { - beanQueryAdapterList.add(cls); + beanQueryAdapterCandidates.add((Class) cls); interesting = true; } if (ServerConfigStartup.class.isAssignableFrom(cls)) { - serverConfigStartupList.add(cls); + serverConfigStartupCandidates.add((Class) 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) cls; interesting = true; } if (ChangeLogRegister.class.isAssignableFrom(cls)) { - changeLogRegisterClass = cls; + changeLogRegisterClass = (Class) cls; interesting = true; } if (ChangeLogPrepare.class.isAssignableFrom(cls)) { - changeLogPrepareClass = cls; + changeLogPrepareClass = (Class) cls; interesting = true; } if (ReadAuditPrepare.class.isAssignableFrom(cls)) { - readAuditPrepareClass = cls; + readAuditPrepareClass = (Class) cls; interesting = true; } if (ReadAuditLogger.class.isAssignableFrom(cls)) { - readAuditLoggerClass = cls; + readAuditLoggerClass = (Class) cls; interesting = true; } 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 c29a7b6e3..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 @@ -178,8 +178,6 @@ public class DeployBeanDescriptor { private String name; - private boolean processedRawSqlExtend; - private ChangeLogFilter changeLogFilter; private String dbComment; 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> 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> 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); From b848587cd3203721fb472784e761a6181fc93982 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 5 Oct 2016 21:51:04 +0200 Subject: [PATCH 4/5] ADD: Testcases for PostConstruct --- .../lifecycle/TestLifecycleAnnotatedBean.java | 58 +++++++++++++++++++ .../model/basic/EBasicWithLifecycle.java | 11 ++++ 2 files changed, 69 insertions(+) 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; } From b01ddea7811ccfa1cf3696ab14d6e2d8054adc74 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 5 Oct 2016 21:51:42 +0200 Subject: [PATCH 5/5] FIX: fixed non-deterministic failing TestHistoryInsert testcase --- src/test/java/com/avaje/tests/history/TestHistoryInsert.java | 1 + 1 file changed, 1 insertion(+) 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);