CAY-2358 NPE when callbacks invoked on null objects

Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/65ebda99
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/65ebda99
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/65ebda99

Branch: refs/heads/master
Commit: 65ebda996b5dff7b9887a45fc1d5c5759192f8e3
Parents: 8452d41
Author: Nikita Timofeev <stari...@gmail.com>
Authored: Thu Aug 31 12:20:49 2017 +0300
Committer: Nikita Timofeev <stari...@gmail.com>
Committed: Thu Aug 31 12:20:49 2017 +0300

----------------------------------------------------------------------
 .../reflect/LifecycleCallbackEventHandler.java  | 16 +++++------
 .../org/apache/cayenne/query/EJBQLQueryIT.java  | 29 ++++++++++++++++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |  1 +
 3 files changed, 37 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/65ebda99/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
index db596bb..fd044a6 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
@@ -20,7 +20,6 @@ package org.apache.cayenne.reflect;
 
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.ObjEntity;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -111,14 +110,8 @@ class LifecycleCallbackEventHandler {
      * Registers a callback object to be invoked when a lifecycle event occurs.
      */
     private void addCallback(Class<?> entityClass, AbstractCallback callback) {
-        Collection<AbstractCallback> entityListeners = 
listeners.get(entityClass
-                .getName());
-
-        if (entityListeners == null) {
-            entityListeners = new ArrayList<>(3);
-            listeners.put(entityClass.getName(), entityListeners);
-        }
-
+        Collection<AbstractCallback> entityListeners = listeners
+                .computeIfAbsent(entityClass.getName(), k -> new 
ArrayList<>(3));
         entityListeners.add(callback);
     }
 
@@ -126,6 +119,11 @@ class LifecycleCallbackEventHandler {
      * Invokes callbacks for a given entity object.
      */
     void performCallbacks(Persistent object) {
+        if(object == null) {
+            // this can happen if object resolved to null from some query with 
outer join
+            // (e.g. in EJBQL or SQLTemplate)
+            return;
+        }
 
         // default listeners are invoked first
         if (!defaultListeners.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/65ebda99/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
index 2e148ef..a760f3c 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
@@ -28,6 +28,8 @@ import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.map.EJBQLQueryDescriptor;
 import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.LifecycleEvent;
+import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
 import org.apache.cayenne.testdo.testmap.Artist;
@@ -645,4 +647,31 @@ public class EJBQLQueryIT extends ServerCase {
         }
     }
 
+    @Test
+    public void testNullObjectsCallback() throws Exception {
+        tArtist.insert(1, "a1");
+        tArtist.insert(2, "a2");
+        tArtist.insert(3, "a3");
+
+        tPainting.insert(1, 2, "title1");
+        tPainting.insert(2, 1, "title2");
+        tPainting.insert(3, 1, "title3");
+
+        // set callback to be called
+        LifecycleCallbackRegistry registry = runtime
+                .getDataDomain()
+                .getEntityResolver()
+                .getCallbackRegistry();
+        registry.addCallback(LifecycleEvent.POST_LOAD, Painting.class, 
"postAddCallback");
+
+        // select Paintings, where one of it will be null
+        EJBQLQuery queryFullProduct = new EJBQLQuery("select a.paintingArray+ 
from Artist a order by a.artistName");
+        List<Painting> result1 = context.performQuery(queryFullProduct);
+        assertEquals(4, result1.size());
+        assertNull(result1.get(3));
+        for(int i=0; i<3; i++) {
+            assertNotNull(result1.get(i));
+            assertTrue(result1.get(i).isPostAdded());
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/65ebda99/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt 
b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 77e14a3..d80050a 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -38,6 +38,7 @@ CAY-2353 Broken paginated column select with only one entity 
in the result
 CAY-2354 DbGenerator.runGenerator must commit its connection
 CAY-2356 EJBQL: Incorrect COUNT() on outer joined table
 CAY-2357 Generic select queries silently convert result to nulls if no PK 
column found
+CAY-2358 NPE when callbacks invoked on null objects
 
 ----------------------------------
 Release: 4.0.B1

Reply via email to