cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject svn commit: r1240798 - in /cayenne/main/trunk: docs/doc/src/main/resources/ framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/ framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/ framework/cayenne-li...
Date Sun, 05 Feb 2012 20:27:00 GMT
Author: aadamchik
Date: Sun Feb  5 20:26:58 2012
New Revision: 1240798

URL: http://svn.apache.org/viewvc?rev=1240798&view=rev
Log:
CAY-1662 @Auditable and @AuditableChild annotations should support 'ignoredProperties' parameter and exclude changes to runtime relationships

Added:
    cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java
Modified:
    cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
    cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java
    cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml

Modified: cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt (original)
+++ cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt Sun Feb  5 20:26:58 2012
@@ -33,6 +33,7 @@ CAY-1653 Improving DefaultEventManager c
 CAY-1654 Can't switch DataSource Factory on certain projects
 CAY-1656 cayenne generate extra data in xml model when change DataNode factory
 CAY-1657 Expression: in-memory comparison requires persistent to be in the same context as exp parameters to match
+CAY-1662 @Auditable and @AuditableChild annotations should support 'ignoredProperties' parameter and exclude changes to runtime relationships
 
 Bug Fixes Since 3.1M3:
 

Modified: cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java (original)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/Auditable.java Sun Feb  5 20:26:58 2012
@@ -36,4 +36,5 @@ import java.lang.annotation.Target;
 @Inherited
 public @interface Auditable {
 
+    String[] ignoredProperties() default {};
 }

Modified: cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java (original)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableChild.java Sun Feb  5 20:26:58 2012
@@ -43,4 +43,6 @@ public @interface AuditableChild {
      * object that should be audited when annotated object is changed.
      */
     String value();
+    
+    String[] ignoredProperties() default {};
 }

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableEntityDescriptor.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,81 @@
+/*****************************************************************
+ *   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.cayenne.lifecycle.audit;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.lifecycle.changeset.ChangeSet;
+import org.apache.cayenne.lifecycle.changeset.ChangeSetFilter;
+import org.apache.cayenne.lifecycle.changeset.PropertyChange;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+
+class AuditableEntityDescriptor {
+
+    private Collection<String> ignoredProperties;
+
+    AuditableEntityDescriptor(ObjEntity auditableEntity, String[] ignoredProperties) {
+
+        this.ignoredProperties = new HashSet<String>();
+
+        // ignore runtime relationships
+        for (ObjRelationship relationship : auditableEntity.getRelationships()) {
+            if (relationship.isRuntime()) {
+                this.ignoredProperties.add(relationship.getName());
+            }
+        }
+
+        // ignore explicitly specified properties
+        if (ignoredProperties != null) {
+            for (String property : ignoredProperties) {
+                this.ignoredProperties.add(property);
+            }
+        }
+    }
+
+    boolean auditableChange(Persistent object) {
+        if (ignoredProperties.isEmpty()) {
+            return true;
+        }
+
+        ChangeSet changeSet = ChangeSetFilter.preCommitChangeSet();
+        if (changeSet == null) {
+            throw new CayenneRuntimeException(
+                    "Required ChangeSetFilter is not installed, or is in the wrong place in the filter chain.");
+        }
+
+        Map<String, PropertyChange> changes = changeSet.getChanges(object);
+
+        if (changes.size() > ignoredProperties.size()) {
+            return true;
+        }
+
+        for (String key : changes.keySet()) {
+            if (!ignoredProperties.contains(key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java (original)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableFilter.java Sun Feb  5 20:26:58 2012
@@ -18,31 +18,44 @@
  ****************************************************************/
 package org.apache.cayenne.lifecycle.audit;
 
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
 import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.DataChannelFilter;
 import org.apache.cayenne.DataChannelFilterChain;
 import org.apache.cayenne.DataObject;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.Persistent;
 import org.apache.cayenne.QueryResponse;
 import org.apache.cayenne.annotation.PostPersist;
 import org.apache.cayenne.annotation.PostRemove;
 import org.apache.cayenne.annotation.PostUpdate;
 import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.lifecycle.changeset.ChangeSetFilter;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.query.Query;
 
 /**
  * A {@link DataChannelFilter} that enables audit of entities annotated with
- * {@link Auditable} and {@link AuditableChild}.
+ * {@link Auditable} and {@link AuditableChild}. Note that this filter relies on
+ * {@link ChangeSetFilter} presence in the DataDomain filter chain to be able to analyze
+ * ignored properties.
  * 
  * @since 3.1
  */
 public class AuditableFilter implements DataChannelFilter {
 
     private ThreadLocal<AuditableAggregator> threadAggregator;
+    private ConcurrentMap<String, AuditableEntityDescriptor> entityDescriptors;
     protected AuditableProcessor processor;
+    protected EntityResolver entityResolver;
 
-    public AuditableFilter(AuditableProcessor processor) {
+    public AuditableFilter(EntityResolver entityResolver, AuditableProcessor processor) {
         this.processor = processor;
+        this.entityResolver = entityResolver;
+        this.entityDescriptors = new ConcurrentHashMap<String, AuditableEntityDescriptor>();
         this.threadAggregator = new ThreadLocal<AuditableAggregator>();
     }
 
@@ -122,7 +135,9 @@ public class AuditableFilter implements 
 
     @PostUpdate(entityAnnotations = Auditable.class)
     void updateAudit(Object object) {
-        getAggregator().audit(object, AuditableOperation.UPDATE);
+        if (isAuditableUpdate(object, false)) {
+            getAggregator().audit(object, AuditableOperation.UPDATE);
+        }
     }
 
     // only catching child updates... child insert/delete presumably causes an event on
@@ -131,15 +146,20 @@ public class AuditableFilter implements 
     @PostUpdate(entityAnnotations = AuditableChild.class)
     void updateAuditChild(Object object) {
 
-        Object parent = getParent(object);
+        if (isAuditableUpdate(object, true)) {
 
-        if (parent != null) {
-            updateAudit(parent);
-        }
-        else {
-            // TODO: maybe log this fact... shouldn't normally happen, but I can imagine
-            // certain combinations of object graphs, disconnected relationships, delete
-            // rules, etc. may cause this
+            Object parent = getParent(object);
+
+            if (parent != null) {
+                // not calling 'updateAudit' to skip checking 'isAuditableUpdate' on
+                // parent
+                getAggregator().audit(parent, AuditableOperation.UPDATE);
+            }
+            else {
+                // TODO: maybe log this fact... shouldn't normally happen, but I can
+                // imagine certain combinations of object graphs, disconnected
+                // relationships, delete rules, etc. may cause this
+            }
         }
     }
 
@@ -164,4 +184,47 @@ public class AuditableFilter implements 
 
         return dataObject.readNestedProperty(annotation.value());
     }
+
+    protected boolean isAuditableUpdate(Object object, boolean child) {
+        AuditableEntityDescriptor descriptor = getEntityDescriptor(object, child);
+        return descriptor.auditableChange((Persistent) object);
+    }
+
+    private AuditableEntityDescriptor getEntityDescriptor(Object object, boolean child) {
+
+        ObjEntity entity = entityResolver.lookupObjEntity(object);
+
+        AuditableEntityDescriptor descriptor = entityDescriptors.get(entity.getName());
+        if (descriptor == null) {
+
+            String[] ignoredProperties;
+
+            if (child) {
+                AuditableChild annotation = object.getClass().getAnnotation(
+                        AuditableChild.class);
+                ignoredProperties = annotation != null
+                        ? annotation.ignoredProperties()
+                        : null;
+            }
+            else {
+                Auditable annotation = object.getClass().getAnnotation(Auditable.class);
+                ignoredProperties = annotation != null
+                        ? annotation.ignoredProperties()
+                        : null;
+            }
+
+            descriptor = new AuditableEntityDescriptor(entity, ignoredProperties);
+
+            AuditableEntityDescriptor existingDescriptor = entityDescriptors.putIfAbsent(
+                    entity.getName(),
+                    descriptor);
+
+            if (existingDescriptor != null) {
+                descriptor = existingDescriptor;
+            }
+        }
+
+        return descriptor;
+
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java (original)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/audit/AuditableProcessor.java Sun Feb  5 20:26:58 2012
@@ -19,7 +19,7 @@
 package org.apache.cayenne.lifecycle.audit;
 
 /**
- * A superclass of application specific handlers of the {@link Auditable} mixin that
+ * A superclass of application specific handlers of the {@link Auditable} annotation, that
  * provides basic needed callbacks.
  * 
  * @since 3.1

Modified: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilterTest.java Sun Feb  5 20:26:58 2012
@@ -18,25 +18,41 @@
  ****************************************************************/
 package org.apache.cayenne.lifecycle.audit;
 
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import junit.framework.TestCase;
 
+import org.apache.cayenne.CayenneDataObject;
 import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.DataChannelFilterChain;
 import org.apache.cayenne.DataObject;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 public class AuditableFilterTest extends TestCase {
 
+    private AuditableProcessor processor;
+    private EntityResolver resolver;
+
+    @Override
+    protected void setUp() throws Exception {
+        processor = mock(AuditableProcessor.class);
+        resolver = mock(EntityResolver.class);
+        
+        ObjEntity objectEntity = new ObjEntity("CayenneDataObject");
+        when(resolver.lookupObjEntity(any(Object.class))).thenReturn(objectEntity);
+    }
+
     public void testInsertAudit() {
-        AuditableProcessor processor = mock(AuditableProcessor.class);
 
-        AuditableFilter filter = new AuditableFilter(processor);
+        AuditableFilter filter = new AuditableFilter(resolver, processor);
         Object audited = new Object();
         filter.insertAudit(audited);
         filter.postSync();
@@ -45,9 +61,8 @@ public class AuditableFilterTest extends
     }
 
     public void testDeleteAudit() {
-        AuditableProcessor processor = mock(AuditableProcessor.class);
 
-        AuditableFilter filter = new AuditableFilter(processor);
+        AuditableFilter filter = new AuditableFilter(resolver, processor);
         Object audited = new Object();
         filter.deleteAudit(audited);
         filter.postSync();
@@ -56,10 +71,9 @@ public class AuditableFilterTest extends
     }
 
     public void testUpdateAudit() {
-        AuditableProcessor processor = mock(AuditableProcessor.class);
 
-        AuditableFilter filter = new AuditableFilter(processor);
-        Object audited = new Object();
+        AuditableFilter filter = new AuditableFilter(resolver, processor);
+        Object audited = new CayenneDataObject();
         filter.updateAudit(audited);
         filter.postSync();
 
@@ -67,12 +81,12 @@ public class AuditableFilterTest extends
     }
 
     public void testUpdateAuditChild() {
-        AuditableProcessor processor = mock(AuditableProcessor.class);
 
-        AuditableFilter filter = new AuditableFilter(processor);
+        AuditableFilter filter = new AuditableFilter(resolver, processor);
 
         Object auditedParent = new Object();
         DataObject audited = new MockAuditableChild();
+        audited.setObjectId(new ObjectId("MockAuditableChild", "a", 1));
         audited.writeProperty("parent", auditedParent);
         filter.updateAuditChild(audited);
         filter.postSync();
@@ -81,9 +95,8 @@ public class AuditableFilterTest extends
     }
 
     public void testOnSyncPassThrough() {
-        AuditableProcessor processor = mock(AuditableProcessor.class);
 
-        AuditableFilter filter = new AuditableFilter(processor);
+        AuditableFilter filter = new AuditableFilter(resolver, processor);
         ObjectContext context = mock(ObjectContext.class);
         GraphDiff changes = mock(GraphDiff.class);
 
@@ -97,13 +110,12 @@ public class AuditableFilterTest extends
     }
 
     public void testOnSyncAuditEventsCollapse() {
-        AuditableProcessor processor = mock(AuditableProcessor.class);
 
-        final AuditableFilter filter = new AuditableFilter(processor);
+        final AuditableFilter filter = new AuditableFilter(resolver, processor);
         ObjectContext context = mock(ObjectContext.class);
         GraphDiff changes = mock(GraphDiff.class);
 
-        final Object auditedParent1 = new Object();
+        final Object auditedParent1 = new CayenneDataObject();
         final DataObject audited11 = new MockAuditableChild();
         audited11.writeProperty("parent", auditedParent1);
         final DataObject audited12 = new MockAuditableChild();

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/audit/AuditableFilter_InRuntime_Test.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,271 @@
+/*****************************************************************
+ *   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.cayenne.lifecycle.audit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.cayenne.Cayenne;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.lifecycle.changeset.ChangeSetFilter;
+import org.apache.cayenne.lifecycle.db.Auditable1;
+import org.apache.cayenne.lifecycle.db.Auditable2;
+import org.apache.cayenne.lifecycle.db.AuditableChild1;
+import org.apache.cayenne.lifecycle.db.AuditableChild2;
+import org.apache.cayenne.lifecycle.db.AuditableChild3;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+
+public class AuditableFilter_InRuntime_Test extends TestCase {
+
+    private ServerRuntime runtime;
+
+    private TableHelper auditable1;
+    private TableHelper auditableChild1;
+    private TableHelper auditableChild2;
+
+    private TableHelper auditable2;
+    private TableHelper auditableChild3;
+
+    @Override
+    protected void setUp() throws Exception {
+        runtime = new ServerRuntime("cayenne-lifecycle.xml");
+
+        DBHelper dbHelper = new DBHelper(runtime.getDataSource(null));
+
+        auditable1 = new TableHelper(dbHelper, "AUDITABLE1").setColumns(
+                "ID",
+                "CHAR_PROPERTY1");
+
+        auditableChild1 = new TableHelper(dbHelper, "AUDITABLE_CHILD1").setColumns(
+                "ID",
+                "AUDITABLE1_ID",
+                "CHAR_PROPERTY1");
+
+        auditableChild2 = new TableHelper(dbHelper, "AUDITABLE_CHILD2").setColumns(
+                "ID",
+                "AUDITABLE1_ID",
+                "CHAR_PROPERTY1");
+
+        auditable2 = new TableHelper(dbHelper, "AUDITABLE2").setColumns(
+                "ID",
+                "CHAR_PROPERTY1",
+                "CHAR_PROPERTY2");
+
+        auditableChild3 = new TableHelper(dbHelper, "AUDITABLE_CHILD3").setColumns(
+                "ID",
+                "AUDITABLE2_ID",
+                "CHAR_PROPERTY1",
+                "CHAR_PROPERTY2");
+
+        auditableChild1.deleteAll();
+        auditableChild2.deleteAll();
+        auditable1.deleteAll();
+
+        auditableChild3.deleteAll();
+        auditable2.deleteAll();
+    }
+
+    public void testAudit_IgnoreRuntimeRelationships() throws Exception {
+
+        auditable1.insert(1, "xx");
+        auditable1.insert(2, "yy");
+        auditable1.insert(3, "aa");
+        auditableChild2.insert(1, 1, "zz");
+
+        DataDomain domain = runtime.getDataDomain();
+
+        Processor processor = new Processor();
+
+        AuditableFilter filter = new AuditableFilter(
+                domain.getEntityResolver(),
+                processor);
+        domain.addFilter(filter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+        // prerequisite for BaseAuditableProcessor use
+        ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+        domain.addFilter(changeSetFilter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+        ObjectContext context = runtime.getContext();
+
+        Auditable1 a2 = Cayenne.objectForPK(context, Auditable1.class, 2);
+        AuditableChild2 a21 = Cayenne.objectForPK(context, AuditableChild2.class, 1);
+
+        a21.setParent(a2);
+        a21.setCharProperty1("XYZA");
+        context.commitChanges();
+
+        assertEquals(0, processor.size);
+
+        processor.reset();
+
+        Auditable1 a3 = Cayenne.objectForPK(context, Auditable1.class, 3);
+        a21.setParent(a3);
+        a3.setCharProperty1("12");
+
+        context.commitChanges();
+        assertEquals(1, processor.size);
+        assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a3));
+    }
+
+    public void testAudit_IncludeToManyRelationships() throws Exception {
+
+        auditable1.insert(1, "xx");
+        auditable1.insert(2, "yy");
+        auditableChild1.insert(1, 1, "zz");
+
+        DataDomain domain = runtime.getDataDomain();
+
+        Processor processor = new Processor();
+
+        AuditableFilter filter = new AuditableFilter(
+                domain.getEntityResolver(),
+                processor);
+        domain.addFilter(filter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+        // prerequisite for BaseAuditableProcessor use
+        ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+        domain.addFilter(changeSetFilter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+        ObjectContext context = runtime.getContext();
+
+        Auditable1 a2 = Cayenne.objectForPK(context, Auditable1.class, 2);
+        AuditableChild1 a21 = Cayenne.objectForPK(context, AuditableChild1.class, 1);
+
+        a21.setParent(a2);
+        context.commitChanges();
+
+        assertEquals(2, processor.size);
+
+        assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a2));
+        assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(
+                Cayenne.objectForPK(context, Auditable1.class, 1)));
+    }
+
+    public void testAudit_IgnoreProperties() throws Exception {
+
+        auditable2.insert(1, "P1_1", "P2_1");
+        auditable2.insert(2, "P1_2", "P2_2");
+        auditable2.insert(3, "P1_3", "P2_3");
+
+        DataDomain domain = runtime.getDataDomain();
+
+        Processor processor = new Processor();
+
+        AuditableFilter filter = new AuditableFilter(
+                domain.getEntityResolver(),
+                processor);
+        domain.addFilter(filter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+        // prerequisite for BaseAuditableProcessor use
+        ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+        domain.addFilter(changeSetFilter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+        ObjectContext context = runtime.getContext();
+
+        Auditable2 a1 = Cayenne.objectForPK(context, Auditable2.class, 1);
+        Auditable2 a2 = Cayenne.objectForPK(context, Auditable2.class, 2);
+        Auditable2 a3 = Cayenne.objectForPK(context, Auditable2.class, 3);
+
+        a1.setCharProperty1("__");
+        a2.setCharProperty2("__");
+        a3.setCharProperty1("__");
+        a3.setCharProperty2("__");
+
+        context.commitChanges();
+
+        assertEquals(2, processor.size);
+        assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a2));
+        assertTrue(processor.audited.get(AuditableOperation.UPDATE).contains(a3));
+    }
+
+    public void testAuditableChild_IgnoreProperties() throws Exception {
+
+        auditable2.insert(1, "P1_1", "P2_1");
+        auditable2.insert(2, "P1_2", "P2_2");
+        auditableChild3.insert(1, 1, "C", "D");
+
+        DataDomain domain = runtime.getDataDomain();
+
+        Processor processor = new Processor();
+
+        AuditableFilter filter = new AuditableFilter(
+                domain.getEntityResolver(),
+                processor);
+        domain.addFilter(filter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(filter);
+
+        // prerequisite for BaseAuditableProcessor use
+        ChangeSetFilter changeSetFilter = new ChangeSetFilter();
+        domain.addFilter(changeSetFilter);
+        domain.getEntityResolver().getCallbackRegistry().addListener(changeSetFilter);
+
+        ObjectContext context = runtime.getContext();
+
+        AuditableChild3 ac1 = Cayenne.objectForPK(context, AuditableChild3.class, 1);
+
+        // a change to ignored property should not cause an audit event
+        ac1.setCharProperty1("X_X");
+
+        context.commitChanges();
+        assertEquals(0, processor.size);
+
+        processor.reset();
+        ac1.setCharProperty2("XXXXX");
+        context.commitChanges();
+        assertEquals(1, processor.size);
+    }
+
+    private final class Processor implements AuditableProcessor {
+
+        Map<AuditableOperation, Collection<Object>> audited;
+        int size;
+
+        Processor() {
+            reset();
+        }
+
+        void reset() {
+
+            audited = new EnumMap<AuditableOperation, Collection<Object>>(
+                    AuditableOperation.class);
+
+            for (AuditableOperation op : AuditableOperation.values()) {
+                audited.put(op, new ArrayList<Object>());
+            }
+        }
+
+        public void audit(Object object, AuditableOperation operation) {
+            audited.get(operation).add(object);
+            size++;
+        }
+    }
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable1.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.audit.Auditable;
+import org.apache.cayenne.lifecycle.db.auto._Auditable1;
+
+@Auditable
+public class Auditable1 extends _Auditable1 {
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/Auditable2.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.audit.Auditable;
+import org.apache.cayenne.lifecycle.db.auto._Auditable2;
+
+@Auditable(ignoredProperties = _Auditable2.CHAR_PROPERTY1_PROPERTY)
+public class Auditable2 extends _Auditable2 {
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild1.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.db.auto._AuditableChild1;
+
+public class AuditableChild1 extends _AuditableChild1 {
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild2.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.db.auto._AuditableChild2;
+
+public class AuditableChild2 extends _AuditableChild2 {
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditableChild3.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.audit.AuditableChild;
+import org.apache.cayenne.lifecycle.db.auto._AuditableChild3;
+
+@AuditableChild(value = _AuditableChild3.PARENT_PROPERTY, ignoredProperties = _AuditableChild3.CHAR_PROPERTY1_PROPERTY)
+public class AuditableChild3 extends _AuditableChild3 {
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable1.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,40 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.AuditableChild1;
+
+/**
+ * Class _Auditable1 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 _Auditable1 extends CayenneDataObject {
+
+    public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+    public static final String CHILDREN1_PROPERTY = "children1";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setCharProperty1(String charProperty1) {
+        writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+    }
+    public String getCharProperty1() {
+        return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+    }
+
+    public void addToChildren1(AuditableChild1 obj) {
+        addToManyTarget(CHILDREN1_PROPERTY, obj, true);
+    }
+    public void removeFromChildren1(AuditableChild1 obj) {
+        removeToManyTarget(CHILDREN1_PROPERTY, obj, true);
+    }
+    @SuppressWarnings("unchecked")
+    public List<AuditableChild1> getChildren1() {
+        return (List<AuditableChild1>)readProperty(CHILDREN1_PROPERTY);
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_Auditable2.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,48 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.AuditableChild3;
+
+/**
+ * Class _Auditable2 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 _Auditable2 extends CayenneDataObject {
+
+    public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+    public static final String CHAR_PROPERTY2_PROPERTY = "charProperty2";
+    public static final String CHILDREN_PROPERTY = "children";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setCharProperty1(String charProperty1) {
+        writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+    }
+    public String getCharProperty1() {
+        return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+    }
+
+    public void setCharProperty2(String charProperty2) {
+        writeProperty(CHAR_PROPERTY2_PROPERTY, charProperty2);
+    }
+    public String getCharProperty2() {
+        return (String)readProperty(CHAR_PROPERTY2_PROPERTY);
+    }
+
+    public void addToChildren(AuditableChild3 obj) {
+        addToManyTarget(CHILDREN_PROPERTY, obj, true);
+    }
+    public void removeFromChildren(AuditableChild3 obj) {
+        removeToManyTarget(CHILDREN_PROPERTY, obj, true);
+    }
+    @SuppressWarnings("unchecked")
+    public List<AuditableChild3> getChildren() {
+        return (List<AuditableChild3>)readProperty(CHILDREN_PROPERTY);
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild1.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,35 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.Auditable1;
+
+/**
+ * Class _AuditableChild1 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 _AuditableChild1 extends CayenneDataObject {
+
+    public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+    public static final String PARENT_PROPERTY = "parent";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setCharProperty1(String charProperty1) {
+        writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+    }
+    public String getCharProperty1() {
+        return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+    }
+
+    public void setParent(Auditable1 parent) {
+        setToOneTarget(PARENT_PROPERTY, parent, true);
+    }
+
+    public Auditable1 getParent() {
+        return (Auditable1)readProperty(PARENT_PROPERTY);
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild2.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,35 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.Auditable1;
+
+/**
+ * Class _AuditableChild2 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 _AuditableChild2 extends CayenneDataObject {
+
+    public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+    public static final String PARENT_PROPERTY = "parent";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setCharProperty1(String charProperty1) {
+        writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+    }
+    public String getCharProperty1() {
+        return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+    }
+
+    public void setParent(Auditable1 parent) {
+        setToOneTarget(PARENT_PROPERTY, parent, true);
+    }
+
+    public Auditable1 getParent() {
+        return (Auditable1)readProperty(PARENT_PROPERTY);
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java?rev=1240798&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java (added)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditableChild3.java Sun Feb  5 20:26:58 2012
@@ -0,0 +1,43 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.lifecycle.db.Auditable2;
+
+/**
+ * Class _AuditableChild3 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 _AuditableChild3 extends CayenneDataObject {
+
+    public static final String CHAR_PROPERTY1_PROPERTY = "charProperty1";
+    public static final String CHAR_PROPERTY2_PROPERTY = "charProperty2";
+    public static final String PARENT_PROPERTY = "parent";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setCharProperty1(String charProperty1) {
+        writeProperty(CHAR_PROPERTY1_PROPERTY, charProperty1);
+    }
+    public String getCharProperty1() {
+        return (String)readProperty(CHAR_PROPERTY1_PROPERTY);
+    }
+
+    public void setCharProperty2(String charProperty2) {
+        writeProperty(CHAR_PROPERTY2_PROPERTY, charProperty2);
+    }
+    public String getCharProperty2() {
+        return (String)readProperty(CHAR_PROPERTY2_PROPERTY);
+    }
+
+    public void setParent(Auditable2 parent) {
+        setToOneTarget(PARENT_PROPERTY, parent, true);
+    }
+
+    public Auditable2 getParent() {
+        return (Auditable2)readProperty(PARENT_PROPERTY);
+    }
+
+
+}

Modified: cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml?rev=1240798&r1=1240797&r2=1240798&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml (original)
+++ cayenne/main/trunk/framework/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml Sun Feb  5 20:26:58 2012
@@ -4,6 +4,31 @@
 	 xsi:schemaLocation="http://cayenne.apache.org/schema/3.0/modelMap http://cayenne.apache.org/schema/3.0/modelMap.xsd"
 	 project-version="6">
 	<property name="defaultPackage" value="org.apache.cayenne.lifecycle.db"/>
+	<db-entity name="AUDITABLE1">
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="AUDITABLE2">
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="CHAR_PROPERTY2" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="AUDITABLE_CHILD1">
+		<db-attribute name="AUDITABLE1_ID" type="INTEGER"/>
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="AUDITABLE_CHILD2">
+		<db-attribute name="AUDITABLE1_ID" type="INTEGER"/>
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="AUDITABLE_CHILD3">
+		<db-attribute name="AUDITABLE2_ID" type="INTEGER"/>
+		<db-attribute name="CHAR_PROPERTY1" type="VARCHAR" length="200"/>
+		<db-attribute name="CHAR_PROPERTY2" type="VARCHAR" length="200"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="E1">
 		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
 	</db-entity>
@@ -21,6 +46,23 @@
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="UUID" type="VARCHAR" length="200"/>
 	</db-entity>
+	<obj-entity name="Auditable1" className="org.apache.cayenne.lifecycle.db.Auditable1" dbEntityName="AUDITABLE1">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+	</obj-entity>
+	<obj-entity name="Auditable2" className="org.apache.cayenne.lifecycle.db.Auditable2" dbEntityName="AUDITABLE2">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+		<obj-attribute name="charProperty2" type="java.lang.String" db-attribute-path="CHAR_PROPERTY2"/>
+	</obj-entity>
+	<obj-entity name="AuditableChild1" className="org.apache.cayenne.lifecycle.db.AuditableChild1" dbEntityName="AUDITABLE_CHILD1">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+	</obj-entity>
+	<obj-entity name="AuditableChild2" className="org.apache.cayenne.lifecycle.db.AuditableChild2" dbEntityName="AUDITABLE_CHILD2">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+	</obj-entity>
+	<obj-entity name="AuditableChild3" className="org.apache.cayenne.lifecycle.db.AuditableChild3" dbEntityName="AUDITABLE_CHILD3">
+		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
+		<obj-attribute name="charProperty2" type="java.lang.String" db-attribute-path="CHAR_PROPERTY2"/>
+	</obj-entity>
 	<obj-entity name="E1" className="org.apache.cayenne.lifecycle.db.E1" dbEntityName="E1">
 	</obj-entity>
 	<obj-entity name="E2" className="org.apache.cayenne.lifecycle.db.E2" dbEntityName="E2">
@@ -32,12 +74,35 @@
 	<obj-entity name="UuidRoot1" className="org.apache.cayenne.lifecycle.db.UuidRoot1" dbEntityName="UUID_ROOT1">
 		<obj-attribute name="uuid" type="java.lang.String" db-attribute-path="UUID"/>
 	</obj-entity>
+	<db-relationship name="children1" source="AUDITABLE1" target="AUDITABLE_CHILD1" toMany="true">
+		<db-attribute-pair source="ID" target="AUDITABLE1_ID"/>
+	</db-relationship>
+	<db-relationship name="children2" source="AUDITABLE1" target="AUDITABLE_CHILD2" toMany="true">
+		<db-attribute-pair source="ID" target="AUDITABLE1_ID"/>
+	</db-relationship>
+	<db-relationship name="children" source="AUDITABLE2" target="AUDITABLE_CHILD3" toMany="true">
+		<db-attribute-pair source="ID" target="AUDITABLE2_ID"/>
+	</db-relationship>
+	<db-relationship name="parent" source="AUDITABLE_CHILD1" target="AUDITABLE1" toMany="false">
+		<db-attribute-pair source="AUDITABLE1_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="parent" source="AUDITABLE_CHILD2" target="AUDITABLE1" toMany="false">
+		<db-attribute-pair source="AUDITABLE1_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="parent" source="AUDITABLE_CHILD3" target="AUDITABLE2" toMany="false">
+		<db-attribute-pair source="AUDITABLE2_ID" target="ID"/>
+	</db-relationship>
 	<db-relationship name="root" source="SORT_DEP" target="SORT_ROOT" toMany="false">
 		<db-attribute-pair source="ROOT_ID" target="ID"/>
 	</db-relationship>
 	<db-relationship name="deps" source="SORT_ROOT" target="SORT_DEP" toMany="true">
 		<db-attribute-pair source="ID" target="ROOT_ID"/>
 	</db-relationship>
+	<obj-relationship name="children1" source="Auditable1" target="AuditableChild1" deleteRule="Deny" db-relationship-path="children1"/>
+	<obj-relationship name="children" source="Auditable2" target="AuditableChild3" deleteRule="Deny" db-relationship-path="children"/>
+	<obj-relationship name="parent" source="AuditableChild1" target="Auditable1" deleteRule="Nullify" db-relationship-path="parent"/>
+	<obj-relationship name="parent" source="AuditableChild2" target="Auditable1" deleteRule="Nullify" db-relationship-path="parent"/>
+	<obj-relationship name="parent" source="AuditableChild3" target="Auditable2" deleteRule="Nullify" db-relationship-path="parent"/>
 	<obj-relationship name="root" source="SortDep" target="SortRoot" deleteRule="Nullify" db-relationship-path="root"/>
 	<obj-relationship name="deps" source="SortRoot" target="SortDep" deleteRule="Deny" db-relationship-path="deps"/>
 </data-map>



Mime
View raw message