You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2020/11/19 14:10:29 UTC

[cayenne] 01/04: Added the ability to create callbacks via annotations

This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit c5de542a0f3977dae9835262c898c983b49fbe3e
Author: andreykravchenko <an...@objectstyle.com>
AuthorDate: Thu Nov 5 13:14:42 2020 +0300

    Added the ability to create callbacks via annotations
---
 .../org/apache/cayenne/map/EntityResolver.java     |  22 +++++
 .../java/org/apache/cayenne/map/AnnotationIT.java  |  82 ++++++++++++++++
 .../testdo/annotation/ArtistAnnotation.java        |  70 +++++++++++++
 .../testdo/annotation/auto/_ArtistAnnotation.java  | 108 +++++++++++++++++++++
 .../cayenne/unit/di/server/CayenneProjects.java    |   1 +
 .../cayenne/unit/di/server/SchemaBuilder.java      |   3 +-
 .../test/resources/annotation/cayenne-project.xml  |   7 ++
 .../resources/annotation/datamapAnnotation.map.xml |  17 ++++
 8 files changed, 309 insertions(+), 1 deletion(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
index 8b945da..ebe7171 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.map;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -29,6 +30,7 @@ import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
+import org.apache.cayenne.annotation.*;
 import org.apache.cayenne.reflect.ClassDescriptor;
 import org.apache.cayenne.reflect.ClassDescriptorMap;
 import org.apache.cayenne.reflect.FaultFactory;
@@ -145,6 +147,26 @@ public class EntityResolver implements MappingNamespace, Serializable {
             for (ObjEntity entity : getObjEntities()) {
                 Class<?> entityClass = entity.getJavaClass();
 
+                for (Method m : entityClass.getDeclaredMethods()) {
+                    if (m.isAnnotationPresent(PostAdd.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.POST_ADD, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PrePersist.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.PRE_PERSIST, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PostPersist.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.POST_PERSIST, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PreUpdate.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.PRE_UPDATE, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PostUpdate.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.POST_UPDATE, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PreRemove.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.PRE_REMOVE, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PostRemove.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.POST_REMOVE, entityClass, m.getName());
+                    } else if (m.isAnnotationPresent(PostLoad.class)) {
+                        callbackRegistry.addCallback(LifecycleEvent.POST_LOAD, entityClass, m.getName());
+                    }
+                }
+
                 CallbackDescriptor[] callbacks = entity.getCallbackMap().getCallbacks();
                 for (CallbackDescriptor callback : callbacks) {
                     for (String method : callback.getCallbackMethods()) {
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/map/AnnotationIT.java b/cayenne-server/src/test/java/org/apache/cayenne/map/AnnotationIT.java
new file mode 100644
index 0000000..232cfc4
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/map/AnnotationIT.java
@@ -0,0 +1,82 @@
+/*****************************************************************
+ *   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
+ *
+ *    https://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.cayenne.map;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
+import org.apache.cayenne.testdo.annotation.ArtistAnnotation;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.2
+ */
+@UseServerRuntime(CayenneProjects.ANNOTATION)
+public class AnnotationIT extends ServerCase {
+
+    @Inject
+    private ObjectContext objectContext;
+
+    @Test
+    public void testAvailableCallback() {
+
+        LifecycleCallbackRegistry lifecycleCallbackRegistry = objectContext.getEntityResolver().getCallbackRegistry();
+
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.POST_ADD));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.PRE_PERSIST));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.POST_PERSIST));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.POST_LOAD));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.PRE_UPDATE));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.POST_UPDATE));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.PRE_REMOVE));
+        assertFalse(lifecycleCallbackRegistry.isEmpty(LifecycleEvent.POST_REMOVE));
+    }
+
+    @Test
+    public void testWorkCallback() {
+        ArtistAnnotation artist = objectContext.newObject(ArtistAnnotation.class);
+        assertEquals(artist.getPostCallback(), "testPostAdd");
+        assertNull(artist.getPreCallback());
+
+        objectContext.commitChanges();
+        assertEquals(artist.getPostCallback(), "testPostPersist");
+        assertEquals(artist.getPreCallback(), "testPrePersist");
+
+        artist = ObjectSelect.query(ArtistAnnotation.class).selectFirst(objectContext);
+        assertEquals(artist.getPostCallback(), "testPostLoad");
+
+        artist.setPostCallback(null);
+        objectContext.commitChanges();
+        assertEquals(artist.getPostCallback(), "testPostUpdate");
+        assertEquals(artist.getPreCallback(), "testPreUpdate");
+
+        objectContext.deleteObject(artist);
+        assertEquals(artist.getPreCallback(), "testPreRemove");
+        objectContext.commitChanges();
+        assertEquals(artist.getPostCallback(), "testPostRemove");
+    }
+
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/annotation/ArtistAnnotation.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/annotation/ArtistAnnotation.java
new file mode 100644
index 0000000..6435524
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/annotation/ArtistAnnotation.java
@@ -0,0 +1,70 @@
+/*****************************************************************
+ *   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
+ *
+ *    https://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.cayenne.testdo.annotation;
+
+import org.apache.cayenne.annotation.*;
+import org.apache.cayenne.testdo.annotation.auto._ArtistAnnotation;
+
+public class ArtistAnnotation extends _ArtistAnnotation {
+
+    private static final long serialVersionUID = 1L;
+
+    @PostAdd
+    protected void testPostAdd() {
+        postCallback = "testPostAdd";
+    }
+
+    @PostPersist
+    protected void testPostPersist() {
+        postCallback = "testPostPersist";
+    }
+
+    @PrePersist
+    protected void testPrePersist() {
+        preCallback = "testPrePersist";
+    }
+
+    @PostUpdate
+    protected void testPostUpdate() {
+        postCallback = "testPostUpdate";
+    }
+
+    @PreUpdate
+    protected void testPreUpdate() {
+        preCallback = "testPreUpdate";
+    }
+
+    @PostLoad
+    protected void testPostLoad() {
+        postCallback = "testPostLoad";
+    }
+
+    @PostRemove
+    protected void testPostRemove() {
+        postCallback = "testPostRemove";
+    }
+
+    @PreRemove
+    protected void testPreRemove() {
+        preCallback = "testPreRemove";
+    }
+
+    @Override
+    protected void onPostAdd() { }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/annotation/auto/_ArtistAnnotation.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/annotation/auto/_ArtistAnnotation.java
new file mode 100644
index 0000000..5e52750
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/annotation/auto/_ArtistAnnotation.java
@@ -0,0 +1,108 @@
+package org.apache.cayenne.testdo.annotation.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.exp.property.PropertyFactory;
+import org.apache.cayenne.exp.property.StringProperty;
+
+/**
+ * Class _Artist was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _ArtistAnnotation extends BaseDataObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final StringProperty<String> POST_CALLBACK = PropertyFactory.createString("postCallback", String.class);
+    public static final StringProperty<String> PRE_CALLBACK = PropertyFactory.createString("preCallback", String.class);
+
+    protected String postCallback;
+    protected String preCallback;
+
+
+    public void setPostCallback(String postCallback) {
+        beforePropertyWrite("postCallback", this.postCallback, postCallback);
+        this.postCallback = postCallback;
+    }
+
+    public String getPostCallback() {
+        beforePropertyRead("postCallback");
+        return this.postCallback;
+    }
+
+    public void setPreCallback(String preCallback) {
+        beforePropertyWrite("preCallback", this.preCallback, preCallback);
+        this.preCallback = preCallback;
+    }
+
+    public String getPreCallback() {
+        beforePropertyRead("preCallback");
+        return this.preCallback;
+    }
+
+    protected abstract void onPostAdd();
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "postCallback":
+                return this.postCallback;
+            case "preCallback":
+                return this.preCallback;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "postCallback":
+                this.postCallback = (String)val;
+                break;
+            case "preCallback":
+                this.preCallback = (String)val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.postCallback);
+        out.writeObject(this.preCallback);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        super.readState(in);
+        this.postCallback = (String)in.readObject();
+        this.preCallback = (String)in.readObject();
+    }
+
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
index 3d7d1e5..ccb392e 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
@@ -87,4 +87,5 @@ public class CayenneProjects {
     public static final String LAZY_ATTRIBUTES_PROJECT = "cayenne-lazy-attributes.xml";
     public static final String CAY_2666 = "cay2666/cayenne-cay-2666.xml";
     public static final String CAY_2641 = "cay2641/cayenne-cay-2641.xml";
+    public static final String ANNOTATION = "annotation/cayenne-project.xml";
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
index 8067005..bc154c6 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
@@ -82,7 +82,8 @@ public class SchemaBuilder {
 			"qualified.map.xml", "quoted-identifiers.map.xml", "inheritance-single-table1.map.xml",
 			"inheritance-vertical.map.xml", "oneway-rels.map.xml", "unsupported-distinct-types.map.xml",
 			"array-type.map.xml", "cay-2032.map.xml", "weighted-sort.map.xml", "hybrid-data-object.map.xml",
-			"java8.map.xml", "inheritance-with-enum.map.xml", "lazy-attributes.map.xml", "cay2666/datamap.map.xml", "cay2641/datamapLazy.map.xml" };
+			"java8.map.xml", "inheritance-with-enum.map.xml", "lazy-attributes.map.xml", "cay2666/datamap.map.xml", "cay2641/datamapLazy.map.xml",
+			"annotation/datamapAnnotation.map.xml" };
 
 	// hardcoded dependent entities that should be excluded
 	// if LOBs are not supported
diff --git a/cayenne-server/src/test/resources/annotation/cayenne-project.xml b/cayenne-server/src/test/resources/annotation/cayenne-project.xml
new file mode 100644
index 0000000..92f7f35
--- /dev/null
+++ b/cayenne-server/src/test/resources/annotation/cayenne-project.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain xmlns="http://cayenne.apache.org/schema/10/domain"
+	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain https://cayenne.apache.org/schema/10/domain.xsd"
+	 project-version="10">
+	<map name="datamapAnnotation"/>
+</domain>
diff --git a/cayenne-server/src/test/resources/annotation/datamapAnnotation.map.xml b/cayenne-server/src/test/resources/annotation/datamapAnnotation.map.xml
new file mode 100644
index 0000000..724ede8
--- /dev/null
+++ b/cayenne-server/src/test/resources/annotation/datamapAnnotation.map.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<data-map xmlns="http://cayenne.apache.org/schema/10/modelMap"
+	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap https://cayenne.apache.org/schema/10/modelMap.xsd"
+	 project-version="10">
+	<property name="defaultPackage" value="org.apache.cayenne.testdo.annotation"/>
+	<db-entity name="artist_annotation">
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="PostCallback" type="VARCHAR" length="15"/>
+		<db-attribute name="PreCallback" type="VARCHAR" length="15"/>
+	</db-entity>
+	<obj-entity name="ArtistAnnotation" className="org.apache.cayenne.testdo.annotation.ArtistAnnotation" dbEntityName="artist_annotation">
+		<obj-attribute name="postCallback" type="java.lang.String" db-attribute-path="PostCallback"/>
+		<obj-attribute name="preCallback" type="java.lang.String" db-attribute-path="PreCallback"/>
+		<post-add method-name="onPostAdd"/>
+	</obj-entity>
+</data-map>