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 e1aba5ba1..7bdbea289 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -149,6 +149,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap { private final XmlConfig xmlConfig; + private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory; + /** * Create for a given database dbConfig. */ @@ -177,6 +179,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap { this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly(); + this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory(); this.persistControllerManager = new PersistControllerManager(bootupClasses); this.persistListenerManager = new PersistListenerManager(bootupClasses); this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); @@ -1010,6 +1013,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap { desc.setUpdateChangesOnly(updateChangesOnly); + beanLifecycleAdapterFactory.addLifecycleMethods(desc); + // set bean controller, finder and listener setBeanControllerFinderListener(desc); deplyInherit.process(desc); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java new file mode 100644 index 000000000..a33b53d2e --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanLifecycleAdapterFactory.java @@ -0,0 +1,186 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.lang.reflect.Method; +import java.util.Set; + +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.event.BeanPersistAdapter; +import com.avaje.ebean.event.BeanPersistRequest; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; + +/** + * Helper that looks for methods annotated with lifecycle events and registers an adapter for them. + *

+ * This includes PrePerist, PostPerist, PreUpdate, PostUpdate, PreRemove, PostRemove and PostLoad + * lifecycle events. + *

+ */ +public class BeanLifecycleAdapterFactory { + + private static final Logger logger = LoggerFactory.getLogger(BeanLifecycleAdapterFactory.class); + + /** + * Register a BeanPersistController for methods annotated with lifecycle events. + */ + public void addLifecycleMethods(DeployBeanDescriptor deployDesc) { + + Method[] methods = deployDesc.getBeanType().getMethods(); + + MethodHolder methodHolder = new MethodHolder(); + + for (Method m : methods) { + methodHolder.checkMethod(m); + } + + if (methodHolder.hasListener()) { + deployDesc.addPersistController(new Adapter(methodHolder)); + } + } + + /** + * Holds Methods for the lifecycle events.s + */ + private static class MethodHolder { + + private boolean hasListener; + private Method preInsert; + private Method postInsert; + private Method preUpdate; + private Method postUpdate; + private Method preDelete; + private Method postDelete; + private Method postLoad; + + private boolean hasListener() { + return hasListener; + } + + private void checkMethod(Method method) { + if (method.isAnnotationPresent(PrePersist.class)) { + preInsert = method; + hasListener = true; + } + if (method.isAnnotationPresent(PostPersist.class)) { + postInsert = method; + hasListener = true; + } + + if (method.isAnnotationPresent(PreUpdate.class)) { + preUpdate = method; + hasListener = true; + } + if (method.isAnnotationPresent(PostUpdate.class)) { + postUpdate = method; + hasListener = true; + } + + if (method.isAnnotationPresent(PreRemove.class)) { + preDelete = method; + hasListener = true; + } + if (method.isAnnotationPresent(PostRemove.class)) { + postDelete = method; + hasListener = true; + } + + if (method.isAnnotationPresent(PostLoad.class)) { + postLoad = method; + hasListener = true; + } + + } + } + + /** + * BeanPersistAdapter using reflection to invoke lifecycle methods. + */ + private static class Adapter extends BeanPersistAdapter { + + private final MethodHolder methodHolder; + + private Adapter(MethodHolder methodHolder) { + this.methodHolder = methodHolder; + } + + @Override + public boolean isRegisterFor(Class cls) { + // Not used + return false; + } + + private void invoke(Method method, Object bean) { + try { + method.invoke(bean); + } catch (Exception e) { + logger.error("Error invoking lifecycle adapter", e); + } + } + + private void invoke(Method method, BeanPersistRequest request) { + invoke(method, request.getBean()); + } + + @Override + public boolean preDelete(BeanPersistRequest request) { + if (methodHolder.preDelete != null) { + invoke(methodHolder.preDelete, request); + } + return true; + } + + @Override + public boolean preInsert(BeanPersistRequest request) { + if (methodHolder.preInsert != null) { + invoke(methodHolder.preInsert, request); + } + return true; + } + + @Override + public boolean preUpdate(BeanPersistRequest request) { + if (methodHolder.preUpdate != null) { + invoke(methodHolder.preUpdate, request); + } + return true; + } + + @Override + public void postDelete(BeanPersistRequest request) { + if (methodHolder.postDelete != null) { + invoke(methodHolder.postDelete, request); + } + } + + @Override + public void postInsert(BeanPersistRequest request) { + if (methodHolder.postInsert != null) { + invoke(methodHolder.postInsert, request); + } + } + + @Override + public void postUpdate(BeanPersistRequest request) { + if (methodHolder.postUpdate != null) { + invoke(methodHolder.postUpdate, request); + } + } + + @Override + public void postLoad(Object bean, Set includedProperties) { + if (methodHolder.postLoad != null) { + invoke(methodHolder.postLoad, bean); + } + } + + } +} 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 56ffda4aa..358665e6e 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 @@ -133,9 +133,9 @@ public class DeployBeanDescriptor { */ private Class beanType; - private List persistControllers = new ArrayList(); - private List> persistListeners = new ArrayList>(); - private List queryAdapters = new ArrayList(); + private List persistControllers = new ArrayList(2); + private List> persistListeners = new ArrayList>(2); + private List queryAdapters = new ArrayList(2); private CacheOptions cacheOptions = new CacheOptions(); @@ -147,7 +147,7 @@ public class DeployBeanDescriptor { /** * The table joins for this bean. Server side only. */ - private ArrayList tableJoinList = new ArrayList(); + private ArrayList tableJoinList = new ArrayList(2); /** * Inheritance information. Server side only. diff --git a/src/test/java/com/avaje/tests/lifecycle/TestLifecyleAnnotatedBean.java b/src/test/java/com/avaje/tests/lifecycle/TestLifecyleAnnotatedBean.java new file mode 100644 index 000000000..6b9610fd8 --- /dev/null +++ b/src/test/java/com/avaje/tests/lifecycle/TestLifecyleAnnotatedBean.java @@ -0,0 +1,34 @@ +package com.avaje.tests.lifecycle; + +import org.junit.Assert; +import org.junit.Test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.tests.model.basic.EBasicWithLifecycle; + +public class TestLifecyleAnnotatedBean extends BaseTestCase { + + @Test + public void test() { + + EBasicWithLifecycle bean = new EBasicWithLifecycle(); + bean.setName("hello there"); + + Ebean.getServerCacheManager(); + Ebean.save(bean); + Assert.assertEquals("prePersist,postPersist,", bean.getBuffer()); + + EBasicWithLifecycle beanWasLoaded = Ebean.find(EBasicWithLifecycle.class, bean.getId()); + Assert.assertEquals("postLoad", beanWasLoaded.getBuffer().toString()); + + bean.setName("Changed"); + Ebean.save(bean); + + Ebean.delete(bean); + + Assert.assertEquals("prePersist,postPersist,preUpdate,postUpdate,preRemove,postRemove", bean.getBuffer()); + + } + +} diff --git a/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java b/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java new file mode 100644 index 000000000..1ea6067fe --- /dev/null +++ b/src/test/java/com/avaje/tests/model/basic/EBasicWithLifecycle.java @@ -0,0 +1,92 @@ +package com.avaje.tests.model.basic; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; +import javax.persistence.Table; +import javax.persistence.Version; + +@Entity +@Table(name="e_basic_withlife") +public class EBasicWithLifecycle { + + @Id + Long id; + + String name; + + @Version + Long version; + + transient StringBuilder buffer = new StringBuilder(); + + @PrePersist + public void prePersist() { + buffer.append("prePersist,"); + } + + @PostPersist + public void postPersist() { + buffer.append("postPersist,"); + } + + @PreUpdate + public void preUpdate() { + buffer.append("preUpdate,"); + } + + @PostUpdate + public void postUpdate() { + buffer.append("postUpdate,"); + } + + @PreRemove + public void preRemove() { + buffer.append("preRemove,"); + } + + @PostRemove + public void postRemove() { + buffer.append("postRemove"); + } + + @PostLoad + public void postLoad() { + buffer.append("postLoad"); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getBuffer() { + return buffer.toString(); + } + +}