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 <[email protected]> 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>
