cayenne-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andrus Adamchik <and...@objectstyle.org>
Subject tracking what's changed for a particular object
Date Tue, 14 Dec 2010 20:56:26 GMT
Another piece of code I checked in to cayenne-mixin sandbox. 

People are often asking how to check which object properties have changed, and Cayenne has
this information internally of course. And with a little wrapper around GraphDiff, we can
simplify access to it. This particular implementation is intended to provide this data to
lifecycle callbacks executed before and after commit.

Andrus


Begin forwarded message:
> From: aadamchik@apache.org
> Date: December 14, 2010 10:29:30 PM GMT+02:00
> To: commits@cayenne.apache.org
> Subject: svn commit: r1049252 - in /cayenne/sandbox/cayenne-mixin/trunk: ./ src/main/java/org/apache/cayenne/mixin/changeset/
> Reply-To: dev@cayenne.apache.org
> 
> Author: aadamchik
> Date: Tue Dec 14 20:29:29 2010
> New Revision: 1049252
> 
> URL: http://svn.apache.org/viewvc?rev=1049252&view=rev
> Log:
> changeset package
> 
> Added:
>   cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/
>   cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSet.java
>   cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSetFilter.java
>   cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/GenericChangeSet.java
>   cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/PropertyChange.java
> Modified:
>   cayenne/sandbox/cayenne-mixin/trunk/README.txt
>   cayenne/sandbox/cayenne-mixin/trunk/pom.xml
> 
> Modified: cayenne/sandbox/cayenne-mixin/trunk/README.txt
> URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-mixin/trunk/README.txt?rev=1049252&r1=1049251&r2=1049252&view=diff
> ==============================================================================
> --- cayenne/sandbox/cayenne-mixin/trunk/README.txt (original)
> +++ cayenne/sandbox/cayenne-mixin/trunk/README.txt Tue Dec 14 20:29:29 2010
> @@ -4,6 +4,7 @@ TODO:
> 
> IMPLEMENTED:
> 
> +4. Changeset tracking functionality
> 3. @MixinRelationship and MixinRelationshipFilter to batch-fault and inject related objects
into mixin entity (e.g. Audit entity)
> 2. @Auditable mixin with abstract handler allowing to store audit records in an arbitrary
format.
> 1. @Referenceable mixin and generic UUID processing classes
> 
> Modified: cayenne/sandbox/cayenne-mixin/trunk/pom.xml
> URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-mixin/trunk/pom.xml?rev=1049252&r1=1049251&r2=1049252&view=diff
> ==============================================================================
> --- cayenne/sandbox/cayenne-mixin/trunk/pom.xml (original)
> +++ cayenne/sandbox/cayenne-mixin/trunk/pom.xml Tue Dec 14 20:29:29 2010
> @@ -8,7 +8,7 @@
> 		<version>3.1M1</version>
> 	</parent>
> 	<artifactId>cayenne-mixin</artifactId>
> -	<version>3.1.0.6</version>
> +	<version>3.1.0.7</version>
> 	<name>Library: cayenne-mixin</name>
> 	<packaging>jar</packaging>
> 	<properties>
> 
> Added: cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSet.java
> URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSet.java?rev=1049252&view=auto
> ==============================================================================
> --- cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSet.java
(added)
> +++ cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSet.java
Tue Dec 14 20:29:29 2010
> @@ -0,0 +1,37 @@
> +/*****************************************************************
> + *   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.mixin.changeset;
> +
> +import java.util.Map;
> +
> +import org.apache.cayenne.Persistent;
> +import org.apache.cayenne.graph.GraphDiff;
> +
> +/**
> + * Represents a set of changes to persistent objects corresponding to a certain lifecycle
> + * stage. The changes are presented in a more usable form compared to the internal Cayenne
> + * representation as {@link GraphDiff}. One or more changes to the same property of
the
> + * same object are all combined in a single {@link PropertyChange} instance.
> + */
> +public interface ChangeSet {
> +
> +    public static final String OBJECT_ID_PROPERTY_NAME = "cayenne:objectId";
> +
> +    Map<String, PropertyChange> getChanges(Persistent object);
> +}
> 
> Added: cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSetFilter.java
> URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSetFilter.java?rev=1049252&view=auto
> ==============================================================================
> --- cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSetFilter.java
(added)
> +++ cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/ChangeSetFilter.java
Tue Dec 14 20:29:29 2010
> @@ -0,0 +1,73 @@
> +/*****************************************************************
> + *   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.mixin.changeset;
> +
> +import org.apache.cayenne.DataChannel;
> +import org.apache.cayenne.DataChannelFilter;
> +import org.apache.cayenne.DataChannelFilterChain;
> +import org.apache.cayenne.ObjectContext;
> +import org.apache.cayenne.QueryResponse;
> +import org.apache.cayenne.graph.GraphDiff;
> +import org.apache.cayenne.query.Query;
> +
> +/**
> + * A {@link DataChannelFilter} that provides interested parties with a thread-local
access
> + * to the current commit changeset. It will only return a non-null change set when commit
> + * within the scope of the filter
> + * {@link #onSync(ObjectContext, GraphDiff, int, DataChannelFilterChain)} method. The
> + * filter is intended to be used by pre-commit and post-commit listeners.
> + */
> +public class ChangeSetFilter implements DataChannelFilter {
> +
> +    private static final ThreadLocal<ChangeSet> PRE_COMMIT_CHANGE_SET = new ThreadLocal<ChangeSet>();
> +
> +    public static ChangeSet preCommitChangeSet() {
> +        return PRE_COMMIT_CHANGE_SET.get();
> +    }
> +
> +    @Override
> +    public void init(DataChannel channel) {
> +        // noop..
> +    }
> +
> +    @Override
> +    public QueryResponse onQuery(
> +            ObjectContext originatingContext,
> +            Query query,
> +            DataChannelFilterChain filterChain) {
> +        return filterChain.onQuery(originatingContext, query);
> +    }
> +
> +    @Override
> +    public GraphDiff onSync(
> +            ObjectContext originatingContext,
> +            GraphDiff changes,
> +            int syncType,
> +            DataChannelFilterChain filterChain) {
> +
> +        try {
> +            PRE_COMMIT_CHANGE_SET.set(new GenericChangeSet(changes));
> +            return filterChain.onSync(originatingContext, changes, syncType);
> +        }
> +        finally {
> +            PRE_COMMIT_CHANGE_SET.set(null);
> +        }
> +    }
> +
> +}
> 
> Added: cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/GenericChangeSet.java
> URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/GenericChangeSet.java?rev=1049252&view=auto
> ==============================================================================
> --- cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/GenericChangeSet.java
(added)
> +++ cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/GenericChangeSet.java
Tue Dec 14 20:29:29 2010
> @@ -0,0 +1,137 @@
> +/*****************************************************************
> + *   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.mixin.changeset;
> +
> +import java.util.HashMap;
> +import java.util.Map;
> +
> +import org.apache.cayenne.ObjectId;
> +import org.apache.cayenne.Persistent;
> +import org.apache.cayenne.graph.GraphChangeHandler;
> +import org.apache.cayenne.graph.GraphDiff;
> +
> +/**
> + * A {@link ChangeSet} implemented as a wrapper on top of {@link GraphDiff} of unspecified
> + * nature.
> + * <p>
> + * Synchronization note: While this class is thread safe, but is not generally intended
> + * for use in multi-threaded manner. It is common to use it within a single transaction
> + * thread.
> + */
> +public class GenericChangeSet implements ChangeSet {
> +
> +    private GraphDiff diff;
> +    private Map<ObjectId, Map<String, PropertyChange>> changes;
> +
> +    public GenericChangeSet(GraphDiff diff) {
> +        this.diff = diff;
> +    }
> +
> +    /**
> +     * Returns a map of changes for a given object in its context, keyed by property
name.
> +     * If the object is unchanged, an empty map is returned.
> +     */
> +    @Override
> +    public Map<String, PropertyChange> getChanges(Persistent object) {
> +        return getChanges().get(object.getObjectId());
> +    }
> +
> +    private Map<ObjectId, Map<String, PropertyChange>> getChanges() {
> +        if (changes == null) {
> +            changes = parseDiff();
> +        }
> +
> +        return changes;
> +    }
> +
> +    private Map<ObjectId, Map<String, PropertyChange>> parseDiff() {
> +
> +        final Map<ObjectId, Map<String, PropertyChange>> changes = new HashMap<ObjectId,
Map<String, PropertyChange>>();
> +
> +        diff.apply(new GraphChangeHandler() {
> +
> +            private Map<String, PropertyChange> getChangeMap(Object id) {
> +                Map<String, PropertyChange> map = changes.get(id);
> +
> +                if (map == null) {
> +                    map = new HashMap<String, PropertyChange>();
> +                    changes.put((ObjectId) id, map);
> +                }
> +
> +                return map;
> +            }
> +
> +            PropertyChange getChange(Object id, String property, Object oldValue) {
> +                Map<String, PropertyChange> map = getChangeMap(id);
> +
> +                PropertyChange change = map.get(property);
> +                if (change == null) {
> +                    change = new PropertyChange(property, oldValue);
> +                    map.put(property, change);
> +                }
> +
> +                return change;
> +            }
> +
> +            @Override
> +            public void nodeRemoved(Object nodeId) {
> +                // noop, don't care, we'll still track the changes for deleted objects.
> +            }
> +
> +            @Override
> +            public void nodeCreated(Object nodeId) {
> +                // noop (??)
> +            }
> +
> +            @Override
> +            public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId)
{
> +                // noop... ignoring for now... ideally should at least track to-one
> +            }
> +
> +            @Override
> +            public void arcCreated(Object nodeId, Object targetNodeId, Object arcId)
{
> +                // noop... ignoring for now... ideally should at least track to-one
> +            }
> +
> +            @Override
> +            public void nodePropertyChanged(
> +                    Object nodeId,
> +                    String property,
> +                    Object oldValue,
> +                    Object newValue) {
> +                getChange(nodeId, property, oldValue).setNewValue(newValue);
> +            }
> +
> +            @Override
> +            public void nodeIdChanged(Object nodeId, Object newId) {
> +
> +                // store the same change set under old and new ids to allow lookup before
> +                // and after the commit
> +                Map<String, PropertyChange> map = getChangeMap(nodeId);
> +                changes.put((ObjectId) newId, map);
> +
> +                // record a change for a special ID "property"
> +                getChange(nodeId, OBJECT_ID_PROPERTY_NAME, nodeId).setNewValue(newId);
> +            }
> +
> +        });
> +
> +        return changes;
> +    }
> +}
> 
> Added: cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/PropertyChange.java
> URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/PropertyChange.java?rev=1049252&view=auto
> ==============================================================================
> --- cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/PropertyChange.java
(added)
> +++ cayenne/sandbox/cayenne-mixin/trunk/src/main/java/org/apache/cayenne/mixin/changeset/PropertyChange.java
Tue Dec 14 20:29:29 2010
> @@ -0,0 +1,50 @@
> +/*****************************************************************
> + *   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.mixin.changeset;
> +
> +/**
> + * A change to a single object property.
> + */
> +public class PropertyChange {
> +
> +    private String propertyName;
> +    private Object oldValue;
> +    private Object newValue;
> +
> +    PropertyChange(String propertyName, Object oldValue) {
> +        this.propertyName = propertyName;
> +        this.oldValue = oldValue;
> +    }
> +
> +    public Object getOldValue() {
> +        return oldValue;
> +    }
> +
> +    public Object getNewValue() {
> +        return newValue;
> +    }
> +
> +    public String getPropertyName() {
> +        return propertyName;
> +    }
> +
> +    void setNewValue(Object newValue) {
> +        this.newValue = newValue;
> +    }
> +}
> 
> 
> 


Mime
View raw message