diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index aab402ac3..c0568d944 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -262,6 +262,7 @@ public class ServerConfig { private List findControllers = new ArrayList(); private List persistControllers = new ArrayList(); + private List postLoaders = new ArrayList(); private List persistListeners = new ArrayList(); private List queryAdapters = new ArrayList(); private List bulkTableEventListeners = new ArrayList(); @@ -1660,14 +1661,45 @@ public class ServerConfig { persistControllers.add(beanPersistController); } + /** + * Register a BeanPostLoad instance. + *

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

+ */ + public void add(BeanPostLoad postLoad) { + postLoaders.add(postLoad); + } + + /** + * Return the list of BeanFindController instances. + */ public List getFindControllers() { return findControllers; } + /** + * Set the list of BeanFindController instances. + */ public void setFindControllers(List findControllers) { this.findControllers = findControllers; } + /** + * Return the list of BeanPostLoader instances. + */ + public List getPostLoaders() { + return postLoaders; + } + + /** + * Set the list of BeanPostLoader instances. + */ + public void setPostLoaders(List postLoaders) { + this.postLoaders = postLoaders; + } + /** * Return the BeanPersistController instances. */ diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java b/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java index fc822e947..44b814e92 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistAdapter.java @@ -66,10 +66,4 @@ public abstract class BeanPersistAdapter implements BeanPersistController { public void postUpdate(BeanPersistRequest request) { } - /** - * Does nothing by default. - */ - public void postLoad(Object bean, Set includedProperties) { - } - } diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistController.java b/src/main/java/com/avaje/ebean/event/BeanPersistController.java index 19d211989..d10fe07e3 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistController.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistController.java @@ -106,10 +106,4 @@ public interface BeanPersistController { */ void postDelete(BeanPersistRequest request); - /** - * Called after every each bean is fetched and loaded from the database. You - * can override this to derive some information to set to the bean. - */ - void postLoad(Object bean, Set includedProperties); - } diff --git a/src/main/java/com/avaje/ebean/event/BeanPostLoad.java b/src/main/java/com/avaje/ebean/event/BeanPostLoad.java new file mode 100644 index 000000000..b76f857d3 --- /dev/null +++ b/src/main/java/com/avaje/ebean/event/BeanPostLoad.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.event; + +/** + * Fired after a bean is fetched and loaded from the database. + *

+ * Note that if want to totally change the finding, you need to use a BeanQueryAdapter + * rather than using postLoad(). + *

+ */ +public interface BeanPostLoad { + + /** + * Return true if this BeanPostLoad instance should be registered + * for post load on this entity type. + */ + boolean isRegisterFor(Class cls); + + /** + * Called after every each bean is loaded from the database. You + * can implement this to derive some information to set to the bean. + */ + void postLoad(Object bean); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java index a19b255bf..f6d0f3e00 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java @@ -39,6 +39,8 @@ public class BootupClasses implements ClassPathSearchMatcher { private final List> beanControllerList = new ArrayList>(); + private final List> beanPostLoadList = new ArrayList>(); + private final List> transactionEventListenerList = new ArrayList>(); private final List> beanFindControllerList = new ArrayList>(); @@ -51,6 +53,7 @@ public class BootupClasses implements ClassPathSearchMatcher { private final List findControllerInstances = new ArrayList(); private final List persistControllerInstances = new ArrayList(); + private final List beanPostLoadInstances = new ArrayList(); private final List persistListenerInstances = new ArrayList(); private final List queryAdapterInstances = new ArrayList(); private final List transactionEventListenerInstances = new ArrayList(); @@ -113,6 +116,19 @@ public class BootupClasses implements ClassPathSearchMatcher { } } + /** + * 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()); + } + } + } + /** * Add BeanFindController instances. */ @@ -223,6 +239,21 @@ public class BootupClasses implements ClassPathSearchMatcher { return persistControllerInstances; } + public List getBeanPostLoaders() { + // add class registered BeanPostLoad to the already created instances + for (Class cls : beanPostLoadList) { + try { + BeanPostLoad newInstance = (BeanPostLoad) cls.newInstance(); + beanPostLoadInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanPersistController " + cls; + logger.error(msg, e); + } + } + + return beanPostLoadInstances; + } + public List getTransactionEventListeners() { // add class registered TransactionEventListener to the // already created instances @@ -309,6 +340,11 @@ public class BootupClasses implements ClassPathSearchMatcher { interesting = true; } + if (BeanPostLoad.class.isAssignableFrom(cls)) { + beanPostLoadList.add(cls); + interesting = true; + } + if (TransactionEventListener.class.isAssignableFrom(cls)) { transactionEventListenerList.add(cls); interesting = true; 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 621baf802..a648b8bac 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -242,6 +242,7 @@ public class DefaultContainer implements SpiContainer { BootupClasses bootupClasses = getBootupClasses1(serverConfig); bootupClasses.addPersistControllers(serverConfig.getPersistControllers()); + bootupClasses.addPostLoaders(serverConfig.getPostLoaders()); bootupClasses.addFindControllers(serverConfig.getFindControllers()); bootupClasses.addTransactionEventListeners(serverConfig.getTransactionEventListeners()); bootupClasses.addPersistListeners(serverConfig.getPersistListeners()); 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 3d0133e3b..e7c1ad5ce 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -13,6 +13,7 @@ import com.avaje.ebean.config.dbplatform.IdType; import com.avaje.ebean.event.BeanFindController; import com.avaje.ebean.event.BeanPersistController; import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebean.event.BeanPostLoad; import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebean.meta.MetaBeanInfo; import com.avaje.ebean.meta.MetaQueryPlanStatistic; @@ -151,14 +152,14 @@ public class BeanDescriptor implements MetaBeanInfo { private final String[] properties; private final int propertyCount; - /** - * Intercept pre post on insert,update,delete and postLoad(). Server side - * only. + * Intercept pre post on insert,update, and delete . */ private volatile BeanPersistController persistController; + private final BeanPostLoad beanPostLoad; + /** * Listens for post commit insert update and delete events. */ @@ -185,6 +186,7 @@ public class BeanDescriptor implements MetaBeanInfo { * Derived list of properties that make up the unique id. */ protected final BeanProperty idProperty; + private final int idPropertyIndex; /** @@ -330,6 +332,7 @@ public class BeanDescriptor implements MetaBeanInfo { this.beanFinder = deploy.getBeanFinder(); this.persistController = deploy.getPersistController(); this.persistListener = deploy.getPersistListener(); + this.beanPostLoad = deploy.getPostLoad(); this.queryAdapter = deploy.getQueryAdapter(); this.defaultSelectClause = deploy.getDefaultSelectClause(); @@ -934,12 +937,11 @@ public class BeanDescriptor implements MetaBeanInfo { } /** - * Execute the postLoad if a BeanPersistController exists for this bean. + * Execute the postLoad if a BeanPostLoad exists for this bean. */ - public void postLoad(Object bean, Set includedProperties) { - BeanPersistController c = persistController; - if (c != null) { - c.postLoad(bean, includedProperties); + public void postLoad(Object bean) { + if (beanPostLoad != null) { + beanPostLoad.postLoad(bean); } } 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 8439c22e7..97199b6c0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -88,6 +88,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap { private final PersistControllerManager persistControllerManager; + private final PostLoadManager postLoadManager; + private final BeanFinderManager beanFinderManager; private final PersistListenerManager persistListenerManager; @@ -188,6 +190,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap { this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory(); this.persistControllerManager = new PersistControllerManager(bootupClasses); + this.postLoadManager = new PostLoadManager(bootupClasses); this.persistListenerManager = new PersistListenerManager(bootupClasses); this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); this.beanFinderManager = new BeanFinderManager(bootupClasses); @@ -443,10 +446,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap { int qa = beanQueryAdapterManager.getRegisterCount(); int cc = persistControllerManager.getRegisterCount(); + int pl = postLoadManager.getRegisterCount(); int lc = persistListenerManager.getRegisterCount(); int fc = beanFinderManager.getRegisterCount(); - logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]"); + logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "] BeanPostLoaders[" + pl + "]"); } private void logStatus() { @@ -973,6 +977,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap { private void setBeanControllerFinderListener(DeployBeanDescriptor descriptor) { persistControllerManager.addPersistControllers(descriptor); + postLoadManager.addPostLoad(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 20ff92eb3..70add7faf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java @@ -4,7 +4,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import java.util.Set; import javax.persistence.PersistenceException; import javax.persistence.PostLoad; @@ -17,6 +16,7 @@ import javax.persistence.PreUpdate; import com.avaje.ebean.event.BeanPersistAdapter; import com.avaje.ebean.event.BeanPersistRequest; +import com.avaje.ebean.event.BeanPostLoad; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; /** @@ -35,14 +35,20 @@ public class BeanLifecycleAdapterFactory { Method[] methods = deployDesc.getBeanType().getMethods(); + // look for annotated methods MethodsHolder methodHolder = new MethodsHolder(); - for (Method m : methods) { methodHolder.checkMethod(m); } - if (methodHolder.hasListener()) { - deployDesc.addPersistController(new Adapter(methodHolder)); + if (methodHolder.hasPersistMethods()) { + // has pre/post persist annotated methods + deployDesc.addPersistController(new PersistAdapter(new PersistMethodsHolder(methodHolder))); + } + + if (!methodHolder.postLoads.isEmpty()) { + // has postLoad methods + deployDesc.addPostLoad(new PostLoadAdapter(methodHolder.postLoads)); } } @@ -51,7 +57,7 @@ public class BeanLifecycleAdapterFactory { */ private static class MethodsHolder { - private boolean hasListener; + private boolean hasPersistMethods; private final List preInserts = new ArrayList(); private final List postInserts = new ArrayList(); private final List preUpdates = new ArrayList(); @@ -60,53 +66,88 @@ public class BeanLifecycleAdapterFactory { private final List postDeletes = new ArrayList(); private final List postLoads = new ArrayList(); - private boolean hasListener() { - return hasListener; + /** + * Has one of the pre or post insert update delete annotated methods. + */ + private boolean hasPersistMethods() { + return hasPersistMethods; } + /** + * Check the method for all the annotations we are interested in. + */ private void checkMethod(Method method) { if (method.isAnnotationPresent(PrePersist.class)) { preInserts.add(method); - hasListener = true; + hasPersistMethods = true; } if (method.isAnnotationPresent(PostPersist.class)) { postInserts.add(method); - hasListener = true; + hasPersistMethods = true; } if (method.isAnnotationPresent(PreUpdate.class)) { preUpdates.add(method); - hasListener = true; + hasPersistMethods = true; } if (method.isAnnotationPresent(PostUpdate.class)) { postUpdates.add(method); - hasListener = true; + hasPersistMethods = true; } if (method.isAnnotationPresent(PreRemove.class)) { preDeletes.add(method); - hasListener = true; + hasPersistMethods = true; } if (method.isAnnotationPresent(PostRemove.class)) { postDeletes.add(method); - hasListener = true; + hasPersistMethods = true; } if (method.isAnnotationPresent(PostLoad.class)) { postLoads.add(method); - hasListener = true; } } } + + /** + * Utility method to covert List of Method into array (because we care about performance here). + */ + static Method[] toArray(List methodList) { + return methodList.toArray(new Method[methodList.size()]); + } + + /** + * Holds Methods for the lifecycle events.s + */ + private static class PersistMethodsHolder { + + private final Method[] preInserts; + private final Method[] postInserts; + private final Method[] preUpdates; + private final Method[] postUpdates; + private final Method[] preDeletes; + private final Method[] postDeletes; + + PersistMethodsHolder(MethodsHolder methodsHolder) { + this.preInserts = toArray(methodsHolder.preInserts); + this.preUpdates = toArray(methodsHolder.preUpdates); + this.preDeletes = toArray(methodsHolder.preDeletes); + this.postInserts = toArray(methodsHolder.postInserts); + this.postUpdates = toArray(methodsHolder.postUpdates); + this.postDeletes = toArray(methodsHolder.postDeletes); + } + } + /** * BeanPersistAdapter using reflection to invoke lifecycle methods. */ - private static class Adapter extends BeanPersistAdapter { + private static class PersistAdapter extends BeanPersistAdapter { - private final MethodsHolder methodHolder; + private final PersistMethodsHolder methodHolder; - private Adapter(MethodsHolder methodHolder) { + private PersistAdapter(PersistMethodsHolder methodHolder) { this.methodHolder = methodHolder; } @@ -126,10 +167,9 @@ public class BeanLifecycleAdapterFactory { } } - private void invoke(List methods, BeanPersistRequest request) { - if (methods.isEmpty()) return; - for (Method method : methods) { - invoke(method, request.getBean()); + private void invoke(Method[] methods, BeanPersistRequest request) { + for (int i = 0; i < methods.length; i++) { + invoke(methods[i], request.getBean()); } } @@ -165,12 +205,39 @@ public class BeanLifecycleAdapterFactory { public void postUpdate(BeanPersistRequest request) { invoke(methodHolder.postUpdates, request); } + } + + /** + * BeanPostLoad using reflection to invoke lifecycle methods. + */ + private static class PostLoadAdapter implements BeanPostLoad { + + private final Method[] postLoadMethods; + + private PostLoadAdapter(List postLoadMethods) { + this.postLoadMethods = toArray(postLoadMethods); + } @Override - public void postLoad(Object bean, Set includedProperties) { - if (methodHolder.postLoads.isEmpty()) return; - for (Method method : methodHolder.postLoads) { - invoke(method, bean); + 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 postLoad(Object bean) { + for (int i = 0; i < postLoadMethods.length; i++) { + invoke(postLoadMethods[i], bean); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistController.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistController.java index c00f8e4b1..e2b806d04 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistController.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistController.java @@ -1,13 +1,12 @@ package com.avaje.ebeaninternal.server.deploy; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanPersistRequest; + import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; -import java.util.Set; - -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistRequest; /** * Chains multiple BeanPersistController's together. @@ -19,6 +18,7 @@ public class ChainedBeanPersistController implements BeanPersistController { private static final Sorter SORTER = new Sorter(); private final List list; + private final BeanPersistController[] chain; /** @@ -88,6 +88,7 @@ public class ChainedBeanPersistController implements BeanPersistController { /** * Always returns 0 (not used for this object). */ + @Override public int getExecutionOrder() { return 0; } @@ -95,34 +96,33 @@ public class ChainedBeanPersistController implements BeanPersistController { /** * Always returns false (not used for this object). */ + @Override public boolean isRegisterFor(Class cls) { return false; } + @Override public void postDelete(BeanPersistRequest request) { for (int i = 0; i < chain.length; i++) { chain[i].postDelete(request); } } + @Override public void postInsert(BeanPersistRequest request) { for (int i = 0; i < chain.length; i++) { chain[i].postInsert(request); } } - public void postLoad(Object bean, Set includedProperties) { - for (int i = 0; i < chain.length; i++) { - chain[i].postLoad(bean, includedProperties); - } - } - + @Override public void postUpdate(BeanPersistRequest request) { for (int i = 0; i < chain.length; i++) { chain[i].postUpdate(request); } } + @Override public boolean preDelete(BeanPersistRequest request) { for (int i = 0; i < chain.length; i++) { if (!chain[i].preDelete(request)) { @@ -132,6 +132,7 @@ public class ChainedBeanPersistController implements BeanPersistController { return true; } + @Override public boolean preInsert(BeanPersistRequest request) { for (int i = 0; i < chain.length; i++) { if (!chain[i].preInsert(request)) { @@ -141,6 +142,7 @@ public class ChainedBeanPersistController implements BeanPersistController { return true; } + @Override public boolean preUpdate(BeanPersistRequest request) { for (int i = 0; i < chain.length; i++) { if (!chain[i].preUpdate(request)) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java new file mode 100644 index 000000000..4fdbea86e --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPostLoad.java @@ -0,0 +1,77 @@ +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.event.BeanPostLoad; + +import java.util.ArrayList; +import java.util.List; + +/** + * Handles multiple BeanPostLoad's for a given entity type. + */ +public class ChainedBeanPostLoad implements BeanPostLoad { + + private final List list; + + private final BeanPostLoad[] chain; + + /** + * Construct given the list of BeanPersistController's. + */ + public ChainedBeanPostLoad(List list) { + this.list = list; + this.chain = list.toArray(new BeanPostLoad[list.size()]); + } + + /** + * Register a new BeanPersistController and return the resulting chain. + */ + public ChainedBeanPostLoad register(BeanPostLoad c) { + if (list.contains(c)){ + return this; + } else { + List newList = new ArrayList(); + newList.addAll(list); + newList.add(c); + + return new ChainedBeanPostLoad(newList); + } + } + + /** + * De-register a BeanPersistController and return the resulting chain. + */ + public ChainedBeanPostLoad deregister(BeanPostLoad c) { + if (!list.contains(c)){ + return this; + } else { + ArrayList newList = new ArrayList(); + newList.addAll(list); + newList.remove(c); + + return new ChainedBeanPostLoad(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 BeanPostLoad implementations. + */ + @Override + public void postLoad(Object bean) { + for (int i = 0; i < chain.length; i++) { + chain[i].postLoad(bean); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/PostLoadManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/PostLoadManager.java new file mode 100644 index 000000000..da3e7549a --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/PostLoadManager.java @@ -0,0 +1,42 @@ +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.event.BeanPostLoad; +import com.avaje.ebeaninternal.server.core.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 PostLoadManager { + + private static final Logger logger = LoggerFactory.getLogger(PostLoadManager.class); + + private final List list; + + public PostLoadManager(BootupClasses bootupClasses) { + this.list = bootupClasses.getBeanPostLoaders(); + } + + public int getRegisterCount() { + return list.size(); + } + + /** + * Register BeanPostLoad listeners for a given entity type. + */ + public void addPostLoad(DeployBeanDescriptor deployDesc) { + + for (int i = 0; i < list.size(); i++) { + BeanPostLoad c = list.get(i); + if (c.isRegisterFor(deployDesc.getBeanType())) { + logger.debug("BeanPostLoad on[" + deployDesc.getFullName() + "] " + c.getClass().getName()); + deployDesc.addPostLoad(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 4efb94e33..493fb2438 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 @@ -4,15 +4,35 @@ import com.avaje.ebean.annotation.ConcurrencyMode; import com.avaje.ebean.config.TableName; import com.avaje.ebean.config.dbplatform.IdGenerator; import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebean.event.*; +import com.avaje.ebean.event.BeanFindController; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebean.event.BeanPostLoad; +import com.avaje.ebean.event.BeanQueryAdapter; import com.avaje.ebeaninternal.server.core.CacheOptions; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.*; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; import javax.persistence.Entity; import javax.persistence.MappedSuperclass; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Describes Beans including their deployment information. @@ -101,9 +121,10 @@ public class DeployBeanDescriptor { */ private final Class beanType; - private final List persistControllers = new ArrayList(2); - private final List persistListeners = new ArrayList(2); - private final List queryAdapters = new ArrayList(2); + private final List persistControllers = new ArrayList(); + private final List persistListeners = new ArrayList(); + private final List queryAdapters = new ArrayList(); + private final List postLoaders = new ArrayList(); private final CacheOptions cacheOptions = new CacheOptions(); @@ -402,8 +423,18 @@ public class DeployBeanDescriptor { } /** - * Set the Controller. + * Return the BeanPostLoad (could be a chain of them, 1 or null). */ + public BeanPostLoad getPostLoad() { + if (postLoaders.size() == 0) { + return null; + } else if (postLoaders.size() == 1) { + return postLoaders.get(0); + } else { + return new ChainedBeanPostLoad(postLoaders); + } + } + public void addPersistController(BeanPersistController controller) { persistControllers.add(controller); } @@ -416,6 +447,10 @@ public class DeployBeanDescriptor { queryAdapters.add(queryAdapter); } + public void addPostLoad(BeanPostLoad postLoad) { + postLoaders.add(postLoad); + } + /** * Return the base table. Only properties mapped to the base table are by * default persisted. diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java index cbb02b325..b4876cf14 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -301,7 +301,7 @@ public class SqlTreeNodeBean implements SqlTreeNode { if (readId && !temporalVersions) { createListProxies(localDesc, ctx, localBean); } - localDesc.postLoad(localBean, null); + localDesc.postLoad(localBean); EntityBeanIntercept ebi = localBean._ebean_getIntercept(); ebi.setPersistenceContext(persistenceContext); diff --git a/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java b/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java new file mode 100644 index 000000000..8600d0bce --- /dev/null +++ b/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java @@ -0,0 +1,139 @@ +package com.avaje.ebean.event; + + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.tests.model.basic.EBasicVer; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BeanPersistControllerTest { + + PersistAdapter continuePersistingAdapter = new PersistAdapter(true); + + PersistAdapter stopPersistingAdapter = new PersistAdapter(false); + + @Test + public void testInsertUpdateDelete_given_continuePersistingAdapter() { + + EbeanServer ebeanServer = getEbeanServer(continuePersistingAdapter); + + EBasicVer bean = new EBasicVer(); + bean.setName("testController"); + + ebeanServer.save(bean); + assertThat(continuePersistingAdapter.methodsCalled).hasSize(2); + assertThat(continuePersistingAdapter.methodsCalled).containsExactly("preInsert", "postInsert"); + continuePersistingAdapter.methodsCalled.clear(); + + bean.setName("modified"); + ebeanServer.save(bean); + assertThat(continuePersistingAdapter.methodsCalled).hasSize(2); + assertThat(continuePersistingAdapter.methodsCalled).containsExactly("preUpdate", "postUpdate"); + continuePersistingAdapter.methodsCalled.clear(); + + ebeanServer.delete(bean); + assertThat(continuePersistingAdapter.methodsCalled).hasSize(2); + assertThat(continuePersistingAdapter.methodsCalled).containsExactly("preDelete", "postDelete"); + + } + + @Test + public void testInsertUpdateDelete_given_stopPersistingAdapter() { + + EbeanServer ebeanServer = getEbeanServer(stopPersistingAdapter); + + EBasicVer bean = new EBasicVer(); + bean.setName("testController"); + + ebeanServer.save(bean); + assertThat(stopPersistingAdapter.methodsCalled).hasSize(1); + assertThat(stopPersistingAdapter.methodsCalled).containsExactly("preInsert"); + stopPersistingAdapter.methodsCalled.clear(); + + bean.setName("modified"); + ebeanServer.update(bean); + assertThat(stopPersistingAdapter.methodsCalled).hasSize(1); + assertThat(stopPersistingAdapter.methodsCalled).containsExactly("preUpdate"); + stopPersistingAdapter.methodsCalled.clear(); + + ebeanServer.delete(bean); + assertThat(stopPersistingAdapter.methodsCalled).hasSize(1); + assertThat(stopPersistingAdapter.methodsCalled).containsExactly("preDelete"); + } + + private EbeanServer getEbeanServer(PersistAdapter persistAdapter) { + + ServerConfig config = new ServerConfig(); + + config.setName("h2other"); + config.loadFromProperties(); + + config.setRegister(false); + config.setDefaultServer(false); + config.getClasses().add(EBasicVer.class); + + config.add(persistAdapter); + + + return EbeanServerFactory.create(config); + } + + static class PersistAdapter extends BeanPersistAdapter { + + boolean continueDefaultPersisting; + + List methodsCalled = new ArrayList(); + + /** + * No default constructor so only registered manually. + */ + PersistAdapter(boolean continueDefaultPersisting) { + this.continueDefaultPersisting = continueDefaultPersisting; + } + + @Override + public boolean isRegisterFor(Class cls) { + return true; + } + + @Override + public boolean preDelete(BeanPersistRequest request) { + methodsCalled.add("preDelete"); + return continueDefaultPersisting; + } + + @Override + public boolean preInsert(BeanPersistRequest request) { + methodsCalled.add("preInsert"); + return continueDefaultPersisting; + } + + @Override + public boolean preUpdate(BeanPersistRequest request) { + methodsCalled.add("preUpdate"); + return continueDefaultPersisting; + } + + @Override + public void postDelete(BeanPersistRequest request) { + methodsCalled.add("postDelete"); + } + + @Override + public void postInsert(BeanPersistRequest request) { + methodsCalled.add("postInsert"); + } + + @Override + public void postUpdate(BeanPersistRequest request) { + methodsCalled.add("postUpdate"); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java b/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java new file mode 100644 index 000000000..8278c9647 --- /dev/null +++ b/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java @@ -0,0 +1,95 @@ +package com.avaje.ebean.event; + + +import com.avaje.ebean.BeanState; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.tests.model.basic.EBasicVer; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BeanPostLoadTest { + + PostLoad postLoad = new PostLoad(false); + + @Test + public void testPostLoad() { + + EbeanServer ebeanServer = getEbeanServer(); + + EBasicVer bean = new EBasicVer(); + bean.setName("testPostLoad"); + bean.setDescription("someDescription"); + bean.setOther("other"); + + ebeanServer.save(bean); + + EBasicVer found = ebeanServer.find(EBasicVer.class) + .select("name, other") + .setId(bean.getId()) + .findUnique(); + + assertThat(postLoad.methodsCalled).hasSize(1); + assertThat(postLoad.methodsCalled).containsExactly("postLoad"); + assertThat(postLoad.beanState.getLoadedProps()).containsExactly("id", "name", "other"); + assertThat(postLoad.bean).isSameAs(found); + + ebeanServer.delete(bean); + } + + + private EbeanServer getEbeanServer() { + + ServerConfig config = new ServerConfig(); + + config.setName("h2other"); + config.loadFromProperties(); + + config.setRegister(false); + config.setDefaultServer(false); + config.getClasses().add(EBasicVer.class); + + config.add(postLoad); + + return EbeanServerFactory.create(config); + } + + static class PostLoad implements BeanPostLoad { + + + boolean dummy; + + List methodsCalled = new ArrayList(); + + Object bean; + + BeanState beanState; + + /** + * No default constructor so only registered manually. + */ + PostLoad(boolean dummy) { + this.dummy = dummy; + } + + @Override + public boolean isRegisterFor(Class cls) { + return true; + } + + @Override + public void postLoad(Object bean) { + this.methodsCalled.add("postLoad"); + this.bean = bean; + this.beanState = Ebean.getBeanState(bean); + } + + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/lifecycle/TestLifecyleAnnotatedBean.java b/src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java similarity index 89% rename from src/test/java/com/avaje/tests/lifecycle/TestLifecyleAnnotatedBean.java rename to src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java index ff3f42cd2..6d4f954fc 100644 --- a/src/test/java/com/avaje/tests/lifecycle/TestLifecyleAnnotatedBean.java +++ b/src/test/java/com/avaje/tests/lifecycle/TestLifecycleAnnotatedBean.java @@ -7,14 +7,14 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class TestLifecyleAnnotatedBean extends BaseTestCase { +public class TestLifecycleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePrePersistMethodsWhenSavingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("PrePersist"); - Ebean.getServerCacheManager(); Ebean.save(bean); assertThat(bean.getBuffer()).contains("prePersist1"); @@ -23,10 +23,10 @@ public class TestLifecyleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePostPersistMethodsWhenSavingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("PostPersist"); - Ebean.getServerCacheManager(); Ebean.save(bean); assertThat(bean.getBuffer()).contains("postPersist1"); @@ -35,10 +35,10 @@ public class TestLifecyleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePostLoadMethodsWhenFindingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("PostLoad"); - Ebean.getServerCacheManager(); Ebean.save(bean); EBasicWithLifecycle loaded = Ebean.find(EBasicWithLifecycle.class, bean.getId()); @@ -48,10 +48,10 @@ public class TestLifecyleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePreUpdateMethodsWhenUpdatingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("Persisted"); - Ebean.getServerCacheManager(); Ebean.save(bean); bean.setName("PreUpdate"); @@ -63,10 +63,10 @@ public class TestLifecyleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePostUpdateMethodsWhenUpdatingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("Persisted"); - Ebean.getServerCacheManager(); Ebean.save(bean); bean.setName("PostUpdate"); @@ -78,10 +78,10 @@ public class TestLifecyleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePreRemoveMethodsWhenRemovingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("Persisted"); - Ebean.getServerCacheManager(); Ebean.save(bean); Ebean.delete(bean); @@ -91,10 +91,10 @@ public class TestLifecyleAnnotatedBean extends BaseTestCase { @Test public void shouldExecutePostRemoveMethodsWhenRemovingBean() { + EBasicWithLifecycle bean = new EBasicWithLifecycle(); bean.setName("Persisted"); - Ebean.getServerCacheManager(); Ebean.save(bean); Ebean.delete(bean);