diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java index c16af950e..6003ae064 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java @@ -488,6 +488,42 @@ public class LifecycleEventManager ((AttachListener) listener).afterAttach(ev); } break; + + case LifecycleEvent.AFTER_PERSIST_PERFORMED: + if (responds || listener instanceof PostPersistListener) + { + if (mock) + return Boolean.TRUE; + if (ev == null) + ev = new LifecycleEvent(source, rel, type); + ((PostPersistListener) listener) + .afterPersistPerformed(ev); + } + break; + case LifecycleEvent.BEFORE_UPDATE: + case LifecycleEvent.AFTER_UPDATE_PERFORMED: + if (responds || listener instanceof UpdateListener) { + if (mock) + return Boolean.TRUE; + if (ev == null) + ev = new LifecycleEvent(source, rel, type); + if (type == LifecycleEvent.BEFORE_UPDATE) + ((UpdateListener) listener).beforeUpdate(ev); + else + ((UpdateListener) listener) + .afterUpdatePerformed(ev); + } + break; + case LifecycleEvent.AFTER_DELETE_PERFORMED: + if (responds || listener instanceof PostDeleteListener){ + if (mock) + return Boolean.TRUE; + if (ev == null) + ev = new LifecycleEvent(source, rel, type); + ((PostDeleteListener) listener) + .afterDeletePerformed(ev); + } + break; default: throw new InvalidStateException( _loc.get("unknown-lifecycle-event", diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/event/PostDeleteListener.java b/openjpa-kernel/src/main/java/org/apache/openjpa/event/PostDeleteListener.java new file mode 100644 index 000000000..7273d0c0b --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/event/PostDeleteListener.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.event; + +/** + * @since 1.1.0 + */ +public interface PostDeleteListener { + + /** + * Receives notifications before an update is performed. Differs from + * {@link DeleteListener#afterDelete(LifecycleEvent)} in that the latter + * is called after the delete operation, whereas this is called after the + * delete statements have been sent to the data store. + */ + public void afterDeletePerformed(LifecycleEvent event); +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/event/PostPersistListener.java b/openjpa-kernel/src/main/java/org/apache/openjpa/event/PostPersistListener.java new file mode 100644 index 000000000..76a54c731 --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/event/PostPersistListener.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.event; + +/** + * @since 1.1.0 + */ +public interface PostPersistListener { + + /** + * Receives notifications after a persist operation has been written to the + * data store. Differs from {@link PersistListener#afterPersist} in that + * the latter is called at the end of the persist() operation itself, not + * after the flush. + */ + public void afterPersistPerformed(LifecycleEvent event); +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/event/UpdateListener.java b/openjpa-kernel/src/main/java/org/apache/openjpa/event/UpdateListener.java new file mode 100644 index 000000000..b274521db --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/event/UpdateListener.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.event; + +/** + * @since 1.1.0 + */ +public interface UpdateListener { + + /** + * Receives notifications before an update is performed. Differs from + * {@link StoreListener#beforeStore} in that the latter is called for + * updated and new records, whereas this is only invoked for updated + * records. + */ + public void beforeUpdate(LifecycleEvent event); + + /** + * Receives notifications before an update is performed. Differs from + * {@link StoreListener#afterStore} in that the latter is called for + * updated and new records, whereas this is only invoked for updated + * records, and that this is called after the record is actually flushed + * to the store. + */ + public void afterUpdatePerformed(LifecycleEvent event); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerEntity.java new file mode 100644 index 000000000..f5ffce805 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerEntity.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.GeneratedValue; +import javax.persistence.EntityListeners; + +@Entity +@EntityListeners(value = ListenerImpl.class) +public class EntityListenerEntity implements ListenerTestEntity { + @Id @GeneratedValue + private long id; + + private int value; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/GlobalListenerEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/GlobalListenerEntity.java new file mode 100644 index 000000000..317f85a41 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/GlobalListenerEntity.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.GeneratedValue; +import javax.persistence.EntityListeners; + +import org.apache.openjpa.persistence.callbacks.ListenerImpl; + +@Entity +public class GlobalListenerEntity implements ListenerTestEntity { + @Id @GeneratedValue + private long id; + + private int value; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} \ No newline at end of file diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/ListenerImpl.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/ListenerImpl.java new file mode 100644 index 000000000..72746723f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/ListenerImpl.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +import javax.persistence.PrePersist; +import javax.persistence.PostPersist; +import javax.persistence.PostLoad; +import javax.persistence.PreUpdate; +import javax.persistence.PostUpdate; +import javax.persistence.PreRemove; +import javax.persistence.PostRemove; + +public class ListenerImpl { + + static int prePersistCount; + static int postPersistCount; + static int preUpdateCount; + static int postUpdateCount; + static int preRemoveCount; + static int postRemoveCount; + static int postLoadCount; + + @PrePersist + public void prePersist(Object o) { + prePersistCount++; + } + + @PostPersist + public void postPersist(Object o) { + postPersistCount++; + } + + @PostLoad + public void postLoad(Object o) { + postLoadCount++; + } + + @PreUpdate + public void preUpdate(Object o) { + preUpdateCount++; + } + + @PostUpdate + public void postUpdate(Object o) { + postUpdateCount++; + } + + @PreRemove + public void preRemove(Object o) { + preRemoveCount++; + } + + @PostRemove + public void postRemove(Object o) { + postRemoveCount++; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/ListenerTestEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/ListenerTestEntity.java new file mode 100644 index 000000000..f8ad91e82 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/ListenerTestEntity.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +public interface ListenerTestEntity { + + public long getId(); + + public int getValue(); + + public void setValue(int val); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestEntityListeners.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestEntityListeners.java new file mode 100644 index 000000000..d8684f399 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestEntityListeners.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.persistence.OpenJPAEntityManager; + +public class TestEntityListeners extends SingleEMFTestCase { + + public void setUp() { + setUp(CLEAR_TABLES); + ListenerImpl.prePersistCount = 0; + ListenerImpl.postPersistCount = 0; + ListenerImpl.preUpdateCount = 0; + ListenerImpl.postUpdateCount = 0; + ListenerImpl.preRemoveCount = 0; + ListenerImpl.postRemoveCount = 0; + ListenerImpl.postLoadCount = 0; + } + + @Override + protected String getPersistenceUnitName() { + return "listener-pu"; + } + + public void testEntityListeners() { + helper(true); + } + + public void testGlobalListeners() { + helper(false); + } + + public void helper(boolean entityListeners) { + OpenJPAEntityManager em = emf.createEntityManager(); + try { + em.getTransaction().begin(); + ListenerTestEntity o; + if (entityListeners) + o = new EntityListenerEntity(); + else + o = new GlobalListenerEntity(); + em.persist(o); + + assertStatus(1, 0, 0, 0, 0, 0, 0); + + em.getTransaction().commit(); + long id = o.getId(); + em.close(); + + assertStatus(1, 1, 0, 0, 0, 0, 0); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + if (entityListeners) + o = em.find(EntityListenerEntity.class, id); + else + o = em.find(GlobalListenerEntity.class, id); + + assertNotNull(o); + assertStatus(1, 1, 0, 0, 0, 0, 1); + + o.setValue(o.getValue() + 1); + + em.flush(); + assertStatus(1, 1, 1, 1, 0, 0, 1); + + em.remove(o); + assertStatus(1, 1, 1, 1, 1, 0, 1); + + em.getTransaction().commit(); + + assertStatus(1, 1, 1, 1, 1, 1, 1); + + em.close(); + } finally { + if (em != null && em.getTransaction().isActive()) + em.getTransaction().rollback(); + if (em != null && em.isOpen()) + em.close(); + } + } + + private void assertStatus( + int prePersist, int postPersist, + int preUpdate, int postUpdate, + int preRemove, int postRemove, + int postLoad) { + assertEquals(prePersist, ListenerImpl.prePersistCount); + assertEquals(postPersist, ListenerImpl.postPersistCount); + assertEquals(preUpdate, ListenerImpl.preUpdateCount); + assertEquals(postUpdate, ListenerImpl.postUpdateCount); + assertEquals(preRemove, ListenerImpl.preRemoveCount); + assertEquals(postRemove, ListenerImpl.postRemoveCount); + assertEquals(postLoad, ListenerImpl.postLoadCount); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml b/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml new file mode 100644 index 000000000..475736ebb --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml @@ -0,0 +1,36 @@ + + + + org.apache.openjpa.persistence.callbacks + + true + + + + + + + + + + \ No newline at end of file diff --git a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml index aaadf3e1c..d45f29ff8 100644 --- a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml +++ b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml @@ -79,4 +79,14 @@ + + + META-INF/listener-orm.xml + org.apache.openjpa.persistence.callbacks.EntityListenerEntity + org.apache.openjpa.persistence.callbacks.GlobalListenerEntity + + + + diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceListenerAdapter.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceListenerAdapter.java index 60738b4df..ffeeb93c2 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceListenerAdapter.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceListenerAdapter.java @@ -27,13 +27,16 @@ import org.apache.openjpa.event.LifecycleEvent; import org.apache.openjpa.event.LifecycleEventManager; import org.apache.openjpa.event.LoadListener; import org.apache.openjpa.event.PersistListener; -import org.apache.openjpa.event.StoreListener; +import org.apache.openjpa.event.PostPersistListener; +import org.apache.openjpa.event.UpdateListener; +import org.apache.openjpa.event.PostDeleteListener; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.util.CallbackException; class PersistenceListenerAdapter implements LifecycleEventManager.ListenerAdapter, PersistListener, - LoadListener, StoreListener, DeleteListener { + PostPersistListener, LoadListener, UpdateListener, DeleteListener, + PostDeleteListener { private static final Localizer _loc = Localizer.forPackage (PersistenceListenerAdapter.class); @@ -83,6 +86,10 @@ class PersistenceListenerAdapter } public void afterPersist(LifecycleEvent event) { + throw new UnsupportedOperationException(); + } + + public void afterPersistPerformed(LifecycleEvent event) { makeCallback(event); } @@ -94,11 +101,11 @@ class PersistenceListenerAdapter makeCallback(event); } - public void beforeStore(LifecycleEvent event) { + public void beforeUpdate(LifecycleEvent event) { makeCallback(event); } - public void afterStore(LifecycleEvent event) { + public void afterUpdatePerformed(LifecycleEvent event) { makeCallback(event); } @@ -107,6 +114,10 @@ class PersistenceListenerAdapter } public void afterDelete(LifecycleEvent event) { + throw new UnsupportedOperationException(); + } + + public void afterDeletePerformed(LifecycleEvent event) { makeCallback(event); } }