KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > CayenneContextMergeHandler


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne;
21
22 import org.apache.cayenne.graph.GraphChangeHandler;
23 import org.apache.cayenne.graph.GraphDiff;
24 import org.apache.cayenne.graph.GraphEvent;
25 import org.apache.cayenne.reflect.ArcProperty;
26 import org.apache.cayenne.reflect.ClassDescriptor;
27 import org.apache.cayenne.reflect.Property;
28 import org.apache.cayenne.reflect.ToManyProperty;
29 import org.apache.cayenne.util.Util;
30
31 /**
32  * An object that merges "backdoor" modifications of the object graph coming from the
33  * underlying DataChannel. When doing an update, CayenneContextMergeHandler blocks
34  * broadcasting of GraphManager events.
35  *
36  * @since 1.2
37  * @author Andrus Adamchik
38  */

39 class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListener {
40
41     CayenneContext context;
42     boolean active;
43
44     CayenneContextMergeHandler(CayenneContext context) {
45         this.context = context;
46         this.active = true;
47     }
48
49     // ******* DataChannelListener methods *******
50

51     public void graphChanged(final GraphEvent e) {
52         // process flush
53
if (shouldProcessEvent(e) && e.getDiff() != null) {
54             runWithEventsDisabled(new Runnable JavaDoc() {
55
56                 public void run() {
57                     e.getDiff().apply(CayenneContextMergeHandler.this);
58
59                 }
60             });
61
62             // post event outside of "execute" to make sure it is sent
63
repostAfterMerge(e);
64         }
65     }
66
67     public void graphFlushed(final GraphEvent e) {
68         // TODO (Andrus, 10/17/2005) - there are a few problems with commit processing:
69

70         // 1. Event mechanism reliability:
71
// - events may come out of order (commit and then preceeding flush)
72
// - events may be missing all together (commit arrived, while prior flush did
73
// not)
74
// Possible solution - an "event_version_id" to be used for optimistic locking
75

76         // 2. We don't know if our own dirty objects were committed or not...
77
// For now we will simply merge the changes, and keep the context dirty
78

79         if (shouldProcessEvent(e)) {
80
81             runWithEventsDisabled(new Runnable JavaDoc() {
82
83                 public void run() {
84
85                     if (e.getDiff() != null) {
86                         e.getDiff().apply(CayenneContextMergeHandler.this);
87                     }
88                 }
89             });
90
91             // post event outside of "execute" to make sure it is sent
92
repostAfterMerge(e);
93         }
94     }
95
96     public void graphRolledback(final GraphEvent e) {
97
98         // TODO: andrus, 3/29/2007: per CAY-771, if a LOCAL peer context posted the event,
99
// just ignore it, however if the REMOTE peer reverted the parent remote
100
// DataContext, we need to invalidate stale committed objects...
101
}
102
103     // ******* End DataChannelListener methods *******
104

105     void repostAfterMerge(GraphEvent originalEvent) {
106         // though the subject is CHANGE, "merge" events are really lifecycle.
107
if (context.isLifecycleEventsEnabled()) {
108             context.internalGraphManager().send(
109                     originalEvent.getDiff(),
110                     DataChannel.GRAPH_CHANGED_SUBJECT,
111                     originalEvent.getSource());
112         }
113     }
114
115     /**
116      * Executes merging of the external diff.
117      */

118     void merge(final GraphDiff diff) {
119         runWithEventsDisabled(new Runnable JavaDoc() {
120
121             public void run() {
122                 diff.apply(CayenneContextMergeHandler.this);
123             }
124         });
125     }
126
127     // ******* GraphChangeHandler methods *********
128

129     public void nodeIdChanged(Object JavaDoc nodeId, Object JavaDoc newId) {
130         // do not unregister the node just yet... only put replaced id in deadIds to
131
// remove it later. Otherwise stored operations will not work
132
Object JavaDoc node = context.internalGraphManager().getNode(nodeId);
133
134         if (node != null) {
135             context.internalGraphManager().deadIds().add(nodeId);
136             context.internalGraphManager().registerNode(newId, node);
137
138             if (node instanceof Persistent) {
139                 // inject new id
140
((Persistent) node).setObjectId((ObjectId) newId);
141             }
142         }
143     }
144
145     public void nodeCreated(Object JavaDoc nodeId) {
146         // ignore
147
}
148
149     public void nodeRemoved(Object JavaDoc nodeId) {
150         context.getGraphManager().unregisterNode(nodeId);
151     }
152
153     public void nodePropertyChanged(
154             Object JavaDoc nodeId,
155             String JavaDoc property,
156             Object JavaDoc oldValue,
157             Object JavaDoc newValue) {
158
159         Object JavaDoc object = context.internalGraphManager().getNode(nodeId);
160         if (object != null) {
161
162             // do not override local changes....
163
Property p = propertyForId(nodeId, property);
164             if (Util.nullSafeEquals(p.readPropertyDirectly(object), oldValue)) {
165
166                 p.writePropertyDirectly(object, oldValue, newValue);
167             }
168         }
169     }
170
171     public void arcCreated(Object JavaDoc nodeId, Object JavaDoc targetNodeId, Object JavaDoc arcId) {
172         // null source or target likely means the object is not faulted yet... Faults
173
// shouldn't get disturbed by adding/removing arcs
174

175         Object JavaDoc source = context.internalGraphManager().getNode(nodeId);
176         if (source == null) {
177             // no need to connect non-existent object
178
return;
179         }
180
181         // TODO (Andrus, 10/17/2005) - check for local modifications to avoid
182
// overwriting...
183

184         ArcProperty p = (ArcProperty) propertyForId(nodeId, arcId.toString());
185         if (p.isFault(source)) {
186             return;
187         }
188
189         Object JavaDoc target = context.internalGraphManager().getNode(targetNodeId);
190         if (target == null) {
191             target = context.createFault((ObjectId) targetNodeId);
192         }
193
194         context.internalGraphAction().setArcChangeInProcess(true);
195         try {
196             if (p instanceof ToManyProperty) {
197                 ((ToManyProperty) p).addTarget(source, target, false);
198             }
199             else {
200                 p.writePropertyDirectly(source, null, target);
201             }
202         }
203         finally {
204             context.internalGraphAction().setArcChangeInProcess(false);
205         }
206     }
207
208     public void arcDeleted(Object JavaDoc nodeId, Object JavaDoc targetNodeId, Object JavaDoc arcId) {
209
210         // null source or target likely means the object is not faulted yet... Faults
211
// shouldn't get disturbed by adding/removing arcs
212

213         Object JavaDoc source = context.internalGraphManager().getNode(nodeId);
214         if (source == null) {
215             // no need to disconnect non-existent object
216
return;
217         }
218
219         // (see "TODO" in 'arcCreated')
220
ArcProperty p = (ArcProperty) propertyForId(nodeId, arcId.toString());
221         if (p.isFault(source)) {
222             return;
223         }
224
225         Object JavaDoc target = context.internalGraphManager().getNode(targetNodeId);
226         if (target == null) {
227             target = context.createFault((ObjectId) targetNodeId);
228         }
229
230         context.internalGraphAction().setArcChangeInProcess(true);
231         try {
232             if (p instanceof ToManyProperty) {
233                 ((ToManyProperty) p).removeTarget(source, target, false);
234             }
235             else {
236                 p.writePropertyDirectly(source, target, null);
237             }
238         }
239         finally {
240             context.internalGraphAction().setArcChangeInProcess(false);
241         }
242     }
243
244     private Property propertyForId(Object JavaDoc nodeId, String JavaDoc propertyName) {
245         ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
246                 ((ObjectId) nodeId).getEntityName());
247         return descriptor.getProperty(propertyName);
248     }
249
250     // Returns true if this object is active; an event came from our channel, but did not
251
// originate in it.
252
boolean shouldProcessEvent(GraphEvent e) {
253         // only process events that came from our channel, but did not originate in it
254
// (i.e. likely posted by EventBridge)
255
return active
256                 && e.getSource() == context.getChannel()
257                 && e.getPostedBy() != context
258                 && e.getPostedBy() != context.getChannel();
259     }
260
261     // executes a closure, disabling ObjectContext events for the duration of the
262
// execution.
263

264     private void runWithEventsDisabled(Runnable JavaDoc closure) {
265
266         synchronized (context.internalGraphManager()) {
267             boolean changeEventsEnabled = context.internalGraphManager().changeEventsEnabled;
268             context.internalGraphManager().changeEventsEnabled = false;
269
270             boolean lifecycleEventsEnabled = context.internalGraphManager().lifecycleEventsEnabled;
271             context.internalGraphManager().lifecycleEventsEnabled = false;
272
273             try {
274                 closure.run();
275             }
276             finally {
277                 context.internalGraphManager().changeEventsEnabled = changeEventsEnabled;
278                 context.internalGraphManager().lifecycleEventsEnabled = lifecycleEventsEnabled;
279             }
280         }
281     }
282 }
283
Popular Tags