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)"/>


Reply via email to