Author: mikedd
Date: Tue Mar 30 19:56:08 2010
New Revision: 929237
URL: http://svn.apache.org/viewvc?rev=929237&view=rev
Log:
OPENJPA-732:
Prevent duplicate events from being fired (stored in Sets instead of Lists)
Added:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/Message.java
(with props)
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MessageListenerImpl.java
(with props)
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMessageListeners.java
(with props)
Modified:
openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml
Modified:
openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=929237&r1=929236&r2=929237&view=diff
==============================================================================
---
openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
(original)
+++
openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
Tue Mar 30 19:56:08 2010
@@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -1465,7 +1466,7 @@ public class BrokerImpl
_savepoints = new LinkedMap();
} else {
if (_savepointCache == null)
- save.save(Collections.EMPTY_LIST);
+ save.save(Collections.EMPTY_SET);
else {
save.save(_savepointCache);
_savepointCache.clear();
@@ -2134,7 +2135,7 @@ public class BrokerImpl
if (hasTransactionalObjects())
transStates = _transCache;
else
- transStates = Collections.EMPTY_LIST;
+ transStates = Collections.EMPTY_SET;
// fire after rollback/commit event
Collection mobjs = null;
@@ -2209,7 +2210,7 @@ public class BrokerImpl
// now clear trans cache; keep cleared version rather than
// null to avoid having to re-create the set later; more efficient
- if (transStates != Collections.EMPTY_LIST) {
+ if (transStates != Collections.EMPTY_SET) {
_transCache = (TransactionalCache) transStates;
_transCache.clear();
}
@@ -3257,7 +3258,7 @@ public class BrokerImpl
Object obj;
StateManagerImpl sm;
ClassMetaData meta;
- Collection sms = new ArrayList(objs.size());
+ Collection sms = new LinkedHashSet(objs.size());
List exceps = null;
for (Iterator itr = objs.iterator(); itr.hasNext();) {
obj = itr.next();
@@ -3580,7 +3581,7 @@ public class BrokerImpl
try {
assertActiveTransaction();
- Collection sms = new ArrayList(objs.size());
+ Collection sms = new LinkedHashSet(objs.size());
Object obj;
StateManagerImpl sm;
for (Iterator itr = objs.iterator(); itr.hasNext();) {
@@ -3727,7 +3728,7 @@ public class BrokerImpl
*/
protected Collection getTransactionalStates() {
if (!hasTransactionalObjects())
- return Collections.EMPTY_LIST;
+ return Collections.EMPTY_SET;
return _transCache.copy();
}
@@ -3746,7 +3747,7 @@ public class BrokerImpl
*/
protected Collection getDirtyStates() {
if (!hasTransactionalObjects())
- return Collections.EMPTY_LIST;
+ return Collections.EMPTY_SET;
return _transCache.copyDirty();
}
@@ -3757,8 +3758,8 @@ public class BrokerImpl
*/
protected Collection getPendingTransactionalStates() {
if (_pending == null)
- return Collections.EMPTY_LIST;
- return new ArrayList(_pending);
+ return Collections.EMPTY_SET;
+ return new LinkedHashSet(_pending);
}
/**
@@ -3979,19 +3980,19 @@ public class BrokerImpl
public Collection getPersistedTypes() {
if (_persistedClss == null || _persistedClss.isEmpty())
- return Collections.EMPTY_LIST;
+ return Collections.EMPTY_SET;
return Collections.unmodifiableCollection(_persistedClss);
}
public Collection getUpdatedTypes() {
if (_updatedClss == null || _updatedClss.isEmpty())
- return Collections.EMPTY_LIST;
+ return Collections.EMPTY_SET;
return Collections.unmodifiableCollection(_updatedClss);
}
public Collection getDeletedTypes() {
if (_deletedClss == null || _deletedClss.isEmpty())
- return Collections.EMPTY_LIST;
+ return Collections.EMPTY_SET;
return Collections.unmodifiableCollection(_deletedClss);
}
@@ -4641,12 +4642,12 @@ public class BrokerImpl
*/
public Collection copy() {
if (isEmpty())
- return Collections.EMPTY_LIST;
+ return Collections.EMPTY_SET;
// size may not be entirely accurate due to refs expiring, so
// manually copy each object; doesn't matter this way if size too
// big by some
- List copy = new ArrayList(size());
+ Set copy = new LinkedHashSet(size());
if (_dirty != null)
for (Iterator itr = _dirty.iterator(); itr.hasNext();)
copy.add(itr.next());
@@ -4661,8 +4662,8 @@ public class BrokerImpl
*/
public Collection copyDirty() {
if (_dirty == null || _dirty.isEmpty())
- return Collections.EMPTY_LIST;
- return new ArrayList(_dirty);
+ return Collections.EMPTY_SET;
+ return new LinkedHashSet(_dirty);
}
/**
Added:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/Message.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/Message.java?rev=929237&view=auto
==============================================================================
---
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/Message.java
(added)
+++
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/Message.java
Tue Mar 30 19:56:08 2010
@@ -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 java.util.Date;
+
+import javax.persistence.Basic;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Version;
+
+/**
+ * A very simple persistent entity that holds a "message", has a "created"
field
+ * that is initialized to the time at which the object was created, and an id
+ * field that is initialized to the current time.
+ */
+...@entity
+...@entitylisteners(value = MessageListenerImpl.class)
+public class Message {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ @Basic
+ private String message;
+
+ @Basic
+ private Date created = null;
+
+ @Basic
+ private Date updated = null;
+
+ @Version
+ Integer version;
+
+ public Message() {
+ }
+
+ public Message(String msg) {
+ message = msg;
+ }
+
+ public void setId(long val) {
+ id = val;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setMessage(String msg) {
+ message = msg;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setCreated(Date date) {
+ created = date;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+
+ public Date getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(Date updated) {
+ this.updated = updated;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Message) {
+ Message other = (Message) o;
+ return other.getId() == this.getId();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+}
Propchange:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/Message.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MessageListenerImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MessageListenerImpl.java?rev=929237&view=auto
==============================================================================
---
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MessageListenerImpl.java
(added)
+++
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MessageListenerImpl.java
Tue Mar 30 19:56:08 2010
@@ -0,0 +1,102 @@
+/*
+ * 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 java.util.Date;
+
+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 MessageListenerImpl {
+
+ public static int prePersistCount;
+ public static int postPersistCount;
+ public static int preUpdateCount;
+ public static int postUpdateCount;
+ public static int preRemoveCount;
+ public static int postRemoveCount;
+ public static int postLoadCount;
+
+ @PrePersist
+ public void prePersist(Object o) {
+ prePersistCount++;
+
+ if (o instanceof Message) {
+ ((Message) o).setCreated(new Date());
+ ((Message) o).setUpdated(new Date());
+ }
+ }
+
+ @PostPersist
+ public void postPersist(Object o) {
+ postPersistCount++;
+ }
+
+ @PostLoad
+ public void postLoad(Object o) {
+ postLoadCount++;
+ }
+
+ @PreUpdate
+ public void preUpdate(Object o) {
+ preUpdateCount++;
+
+ if (o instanceof Message) {
+ ((Message) o).setUpdated(new Date());
+ }
+ }
+
+ @PostUpdate
+ public void postUpdate(Object o) {
+ postUpdateCount++;
+ }
+
+ @PreRemove
+ public void preRemove(Object o) {
+ preRemoveCount++;
+ }
+
+ @PostRemove
+ public void postRemove(Object o) {
+ postRemoveCount++;
+ }
+
+ public static void resetCounters() {
+ prePersistCount = 0;
+ postPersistCount = 0;
+ preUpdateCount = 0;
+ postUpdateCount = 0;
+ preRemoveCount = 0;
+ postRemoveCount = 0;
+ postLoadCount = 0;
+ }
+
+ public static String getStates() {
+ return "prePersistCount = " + prePersistCount + ", postPersistCount = "
+ + postPersistCount + ", preUpdateCount = " + preUpdateCount
+ + ", postUpdateCount = " + postUpdateCount + ", preRemoveCount = "
+ + preRemoveCount + ", postRemoveCount = " + postRemoveCount
+ + ", postLoadCount = " + postLoadCount;
+ }
+}
Propchange:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MessageListenerImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMessageListeners.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMessageListeners.java?rev=929237&view=auto
==============================================================================
---
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMessageListeners.java
(added)
+++
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMessageListeners.java
Tue Mar 30 19:56:08 2010
@@ -0,0 +1,190 @@
+/*
+ * 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.Query;
+
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+
+public class TestMessageListeners extends SingleEMFTestCase {
+
+ public void setUp() {
+ setUp(CLEAR_TABLES);
+ }
+
+ @Override
+ protected String getPersistenceUnitName() {
+ return "listener-pu";
+ }
+
+ public void testUpdateInPrePersist() {
+ // Create a new EntityManager from the EntityManagerFactory. The
+ // EntityManager is the main object in the persistence API, and is
+ // used to create, delete, and query objects, as well as access
+ // the current transaction
+ OpenJPAEntityManager em = emf.createEntityManager();
+ try {
+ // Begin a new local transaction so that we can persist a new
entity
+ em.getTransaction().begin();
+
+ MessageListenerImpl.resetCounters();
+
+ // Create and persist a new Message entity
+ Message message = new Message("Hello Persistence!");
+ assertNull("Test message's created field to be null.", message
+ .getCreated());
+ assertNull("Test message's updated field to be null.", message
+ .getUpdated());
+
+ em.persist(message);
+
+ // Pre-persist invoked, created and updated fields set
+ assertStatus(1, 0, 0, 0, 0, 0, 0);
+ assertNotNull("Test message's created field being set.", message
+ .getCreated());
+ assertNotNull("Test message's updated field being set.", message
+ .getUpdated());
+
+ em.flush();
+ // Post-persist invoked
+ assertStatus(1, 1, 0, 0, 0, 0, 0);
+
+ em.clear();
+
+ // Perform a simple query to get the Message
+ Query q = em.createQuery("select m from Message m where m.id="
+ + message.getId());
+ Message m = (Message) q.getSingleResult();
+
+ assertEquals("Test first expected message.", "Hello Persistence!",
+ m.getMessage());
+ assertNotNull("Test message's created field being set.", m
+ .getCreated());
+ assertNotNull("Test message's updated field being set.", m
+ .getUpdated());
+
+ // query trigger a load because em is cleared.
+ assertStatus(1, 1, 0, 0, 0, 0, 1);
+
+ em.getTransaction().commit();
+
+ // since data is flushed, commit data with no event fired.
+ assertStatus(1, 1, 0, 0, 0, 0, 1);
+ } finally {
+ if (em != null && em.getTransaction().isActive())
+ em.getTransaction().rollback();
+ if (em != null && em.isOpen())
+ em.close();
+ }
+ }
+
+ public void testUpdateInPreUpdate() {
+ // Create a new EntityManager from the EntityManagerFactory. The
+ // EntityManager is the main object in the persistence API, and is
+ // used to create, delete, and query objects, as well as access
+ // the current transaction
+ OpenJPAEntityManager em = emf.createEntityManager();
+ try {
+ // Begin a new local transaction so that we can persist a new
entity
+ em.getTransaction().begin();
+
+ MessageListenerImpl.resetCounters();
+
+ // Create and persist a new Message entity
+ Message message = new Message("Hello Persistence!");
+ assertNull("Test message's created field to be null.", message
+ .getCreated());
+ assertNull("Test message's updated field to be null.", message
+ .getUpdated());
+
+ em.persist(message);
+
+ // Pre-persist invoked, created and updated fields set
+ assertStatus(1, 0, 0, 0, 0, 0, 0);
+ assertNotNull("Test message's created field being set.", message
+ .getCreated());
+ assertNotNull("Test message's updated field being set.", message
+ .getUpdated());
+
+ // Perform a simple query to get the Message
+ Query q = em.createQuery("select m from Message m where m.id="
+ + message.getId());
+ Message m = (Message) q.getSingleResult();
+ assertEquals("Test first expected message.", "Hello Persistence!",
+ m.getMessage());
+ assertNotNull("Test message's created field being set.", m
+ .getCreated());
+ assertNotNull("Test message's updated field being set.", m
+ .getUpdated());
+
+ // Query cause flush to occur, hence fire the postPersist event
+ assertStatus(1, 1, 0, 0, 0, 0, 0);
+
+ // Create and persist another new Message entity
+ message = new Message("Hello Persistence 2!");
+ assertNull("Test message's created field to be null.", message
+ .getCreated());
+ assertNull("Test message's updated field to be null.", message
+ .getUpdated());
+
+ em.persist(message);
+
+ // Pre-persist invoked, created and updated fields set
+ assertStatus(2, 1, 0, 0, 0, 0, 0);
+ assertNotNull("Test message's created field being set.", message
+ .getCreated());
+ assertNotNull("Test message's updated field being set.", message
+ .getUpdated());
+
+ em.getTransaction().commit();
+
+ // Complete the 2nd @postPersist
+ //assertStatus(2, 2, 0, 0, 0, 0, 0);
+ // preUpdate and postUpdate are called in 1.2.x but not in 2.0
+ assertStatus(2, 2, 1, 1, 0, 0, 0);
+
+ // Make an update to trigger the pre/postUpdater callbacks
+ em.getTransaction().begin();
+ message = em.find(Message.class,message.getId());
+ message.setMessage("Update field and trigger pre/postUpdate");
+ em.getTransaction().commit();
+
+ // Complete the 2nd @postPersist
+ assertStatus(2, 2, 2, 2, 0, 0, 0);
+
+ } 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, MessageListenerImpl.prePersistCount);
+ assertEquals(postPersist, MessageListenerImpl.postPersistCount);
+ assertEquals(preUpdate, MessageListenerImpl.preUpdateCount);
+ assertEquals(postUpdate, MessageListenerImpl.postUpdateCount);
+ assertEquals(preRemove, MessageListenerImpl.preRemoveCount);
+ assertEquals(postRemove, MessageListenerImpl.postRemoveCount);
+ assertEquals(postLoad, MessageListenerImpl.postLoadCount);
+ }
+}
Propchange:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMessageListeners.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml
URL:
http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml?rev=929237&r1=929236&r2=929237&view=diff
==============================================================================
---
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml
(original)
+++
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/listener-orm.xml
Tue Mar 30 19:56:08 2010
@@ -34,4 +34,7 @@
<entity class="EntityListenerEntity">
<exclude-default-listeners/>
</entity>
+ <entity class="Message">
+ <exclude-default-listeners/>
+ </entity>
</entity-mappings>
\ No newline at end of file
Modified:
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml
URL:
http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml?rev=929237&r1=929236&r2=929237&view=diff
==============================================================================
---
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml
(original)
+++
openjpa/branches/1.0.x/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml
Tue Mar 30 19:56:08 2010
@@ -68,6 +68,7 @@
<mapping-file>META-INF/listener-orm.xml</mapping-file>
<class>org.apache.openjpa.persistence.callbacks.EntityListenerEntity</class>
<class>org.apache.openjpa.persistence.callbacks.GlobalListenerEntity</class>
+ <class>org.apache.openjpa.persistence.callbacks.Message</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)"/>