KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > odmg > Image


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

17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.Collections JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.apache.commons.lang.ClassUtils;
27 import org.apache.ojb.broker.Identity;
28 import org.apache.ojb.broker.IdentityFactory;
29 import org.apache.ojb.broker.OJBRuntimeException;
30 import org.apache.ojb.broker.PersistenceBrokerInternal;
31 import org.apache.ojb.broker.core.proxy.CollectionProxy;
32 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
33 import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
34 import org.apache.ojb.broker.core.proxy.ProxyHelper;
35 import org.apache.ojb.broker.metadata.CollectionDescriptor;
36 import org.apache.ojb.broker.metadata.FieldType;
37 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
38 import org.apache.ojb.broker.util.BrokerHelper;
39 import org.apache.ojb.broker.util.logging.Logger;
40 import org.apache.ojb.broker.util.logging.LoggerFactory;
41
42 /**
43  * This class encapsulates classes used to take persistence capable object
44  * state snapshoot and to detect changed fields or references.
45  *
46  * @version $Id: Image.java,v 1.1.2.2 2005/12/21 22:29:21 tomdz Exp $
47  */

48 public abstract class Image
49 {
50     static Logger log = LoggerFactory.getLogger(Image.class);
51     private long timestamp = System.currentTimeMillis();
52
53     private Image()
54     {
55     }
56
57     boolean illegalImageComparison(Image oldImage)
58     {
59         return timestamp < oldImage.timestamp;
60     }
61
62     public abstract void cleanup(boolean reuse);
63
64     public abstract boolean modified(Image other);
65
66     abstract void referenceProcessing(Image oldImage);
67
68     public void performReferenceDetection(Image oldImage)
69     {
70         if(illegalImageComparison(oldImage))
71         {
72             throw new ImageException("The specified Image object is newer than current one, wrong Image order!");
73         }
74         referenceProcessing(oldImage);
75     }
76
77     //===================================================================
78
// inner class
79
//===================================================================
80
public static class MultipleRef extends Image implements CollectionProxyListener
81     {
82         static final int IS_NORMAL_OBJECT = 11;
83         static final int IS_MATERIALIZED_PROXY = 13;
84         static final int IS_UNMATERIALIZED_PROXY = 17;
85
86         private ImageListener listener;
87         private final CollectionDescriptor cod;
88         private final Object JavaDoc collectionOrArray;
89         private Map JavaDoc references;
90         private int status;
91         private boolean hasTransientIdentity;
92         private boolean isRefreshed;
93
94         public MultipleRef(ImageListener listener, CollectionDescriptor cod, Object JavaDoc collectionOrArray)
95         {
96             this.listener = listener;
97             this.cod = cod;
98             this.collectionOrArray = collectionOrArray;
99             this.isRefreshed = true;
100             this.hasTransientIdentity = false;
101             this.references = Collections.EMPTY_MAP;
102             init();
103         }
104
105         private void init()
106         {
107             CollectionProxy colProxy = ProxyHelper.getCollectionProxy(collectionOrArray);
108             if(colProxy != null)
109             {
110                 if(colProxy.isLoaded())
111                 {
112                     status = IS_MATERIALIZED_PROXY;
113                     /*
114                     TODO: avoid this cast
115                     e.g. change CollectionProxy interface - CollectionProxy should
116                     extend Collection to support Iterator
117                     */

118                     handleReferencedObjects(((Collection JavaDoc) colProxy).iterator());
119                 }
120                 else
121                 {
122                     status = IS_UNMATERIALIZED_PROXY;
123                     if(log.isDebugEnabled()) log.debug("Unmaterialized proxy collection, use proxy listener");
124                     colProxy.addListener(this);
125                 }
126             }
127             else
128             {
129                 status = IS_NORMAL_OBJECT;
130                 if(collectionOrArray != null)
131                 {
132                     Iterator JavaDoc it = BrokerHelper.getCollectionIterator(collectionOrArray);
133                     handleReferencedObjects(it);
134                 }
135             }
136         }
137
138         void handleReferencedObjects(Iterator JavaDoc it)
139         {
140             if(it == null) return;
141             references = new HashMap JavaDoc();
142             if(log.isDebugEnabled()) log.debug("Handle collection references");
143             IdentityFactory idFac = listener.getBroker().serviceIdentity();
144             Identity oid;
145             Object JavaDoc obj;
146             while(it.hasNext())
147             {
148                 obj = it.next();
149                 oid = idFac.buildIdentity(obj);
150                 if(!hasTransientIdentity && oid.isTransient())
151                 {
152                     hasTransientIdentity = true;
153                 }
154                 references.put(oid, obj);
155             }
156         }
157
158         public void cleanup(boolean reuse)
159         {
160             if(log.isDebugEnabled()) log.debug("Cleanup collection image, reuse=" + reuse);
161             if(reuse)
162             {
163                 isRefreshed = false;
164             }
165             else
166             {
167                 if(status == IS_UNMATERIALIZED_PROXY)
168                 {
169                     CollectionProxy colProxy = ProxyHelper.getCollectionProxy(collectionOrArray);
170                     if(colProxy != null)
171                     {
172                         colProxy.removeListener(this);
173                     }
174                 }
175             }
176         }
177
178         void referenceProcessing(Image oldImage)
179         {
180             MultipleRef oldRefs = (MultipleRef) oldImage;
181             if(incommensurableProxies(oldRefs))
182             {
183                 if(isUnmaterializedProxy()) handleReferencedObjects(BrokerHelper.getCollectionIterator(collectionOrArray));
184                 if(oldRefs.isUnmaterializedProxy()) oldRefs.handleReferencedObjects(BrokerHelper.getCollectionIterator(oldRefs.collectionOrArray));
185             }
186             if(!isRefreshed) refreshIdentities();
187             if(!oldRefs.isRefreshed) oldRefs.refreshIdentities();
188
189             // find deleted reference objects
190
if(oldRefs.references.size() > 0)
191             {
192                 Iterator JavaDoc oldIter = oldRefs.references.entrySet().iterator();
193                 while(oldIter.hasNext())
194                 {
195                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) oldIter.next();
196                     Identity oldOid = (Identity) entry.getKey();
197                     /*
198                     search for deleted objects: if in the new image an object
199                     from the old image is not contained, we found a deleted object
200                     */

201                     if(!isUnmaterializedProxy() && !containsReference(oldOid))
202                     {
203                         listener.deletedXToN(cod, entry.getValue(), oldOid);
204                     }
205                 }
206             }
207
208             // find new reference objects
209
if(references.size() > 0)
210             {
211                 Iterator JavaDoc newIter = references.entrySet().iterator();
212                 while(newIter.hasNext())
213                 {
214                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) newIter.next();
215                     Identity newOid = (Identity) entry.getKey();
216                     /*
217                     search for added objects: if in the old image an object
218                     from the new image is not contained, we found a added object
219                     */

220                     if(!oldRefs.containsReference(newOid))
221                     {
222                         listener.addedXToN(cod, entry.getValue(), newOid);
223                     }
224                 }
225             }
226         }
227
228         /**
229          * To detect deleted (added) collection objects it's necessary iterate over the old (new) image collection.
230          * If the old (new) image collection is a unmaterialized proxy we have to check if the new (old) image collection
231          * is the same proxy instance or not.
232          * E.g. if the user exchange one another the unmaterialized proxy collection objects of two main objects,
233          * then both proxy need to be materialized to assign the changed FK field values.
234          */

235         private boolean incommensurableProxies(MultipleRef oldImage)
236         {
237             boolean result = false;
238             // deleted objects
239
if(oldImage.isUnmaterializedProxy() || isUnmaterializedProxy())
240             {
241                 result = !collectionOrArray.equals(oldImage.collectionOrArray);
242             }
243             return result;
244         }
245
246         private void refreshIdentities()
247         {
248             // if no transient identities are used, nothing to do
249
if(hasTransientIdentity && references.size() > 0)
250             {
251                 hasTransientIdentity = false;
252                 // we need independent key list from Map
253
List JavaDoc list = new ArrayList JavaDoc(references.keySet());
254                 IdentityFactory idFac = listener.getBroker().serviceIdentity();
255                 Identity oid, newOid;
256                 Object JavaDoc obj;
257                 for(int i = 0; i < list.size(); i++)
258                 {
259                     oid = (Identity) list.get(i);
260                     if(oid.isTransient())
261                     {
262                         obj = references.remove(oid);
263                         newOid = idFac.buildIdentity(obj);
264                         references.put(newOid, obj);
265                         if(!hasTransientIdentity && oid.isTransient())
266                         {
267                             hasTransientIdentity = true;
268                         }
269                     }
270                 }
271                 isRefreshed = true;
272             }
273         }
274
275         /**
276          * Always return 'false', because changed 1:n or m:n references do not
277          * affect the main object.
278          */

279         public boolean modified(Image other)
280         {
281             return false;
282         }
283
284         boolean containsReference(Identity oid)
285         {
286             if(!isRefreshed) refreshIdentities();
287             return references.containsKey(oid);
288         }
289
290         Map JavaDoc getIdentityReferenceObjectMap()
291         {
292             if(!isRefreshed) refreshIdentities();
293             return references;
294         }
295
296         boolean isMaterializedProxy()
297         {
298             return status == IS_MATERIALIZED_PROXY;
299         }
300
301         boolean isUnmaterializedProxy()
302         {
303             return status == IS_UNMATERIALIZED_PROXY;
304         }
305
306
307         // CollectionProxy Listener methods
308
//---------------------------------
309
public void beforeLoading(CollectionProxyDefaultImpl colProxy)
310         {
311             //noop
312
}
313
314         public void afterLoading(CollectionProxyDefaultImpl colProxy)
315         {
316             if(status == IS_UNMATERIALIZED_PROXY)
317             {
318                 status = IS_MATERIALIZED_PROXY;
319                 handleReferencedObjects(colProxy.iterator());
320                 colProxy.removeListener(this);
321             }
322         }
323
324         public String JavaDoc toString()
325         {
326             return ClassUtils.getShortClassName(this.getClass()) + "[references-size="
327                     + (references != null ? "" + references.size() : "undefined") + "]";
328         }
329     }
330
331     //===================================================================
332
// inner class
333
//===================================================================
334
public static class SingleRef extends Image
335     {
336         private Object JavaDoc referenceObjOrProxy;
337         private Identity oid = null;
338         private final ImageListener listener;
339         private final ObjectReferenceDescriptor ord;
340
341         public SingleRef(ImageListener listener, ObjectReferenceDescriptor ord, Object JavaDoc reference)
342         {
343             this.listener = listener;
344             this.ord = ord;
345             this.referenceObjOrProxy = reference;
346         }
347
348         public void cleanup(boolean reuse)
349         {
350             if(!reuse)
351             {
352                 referenceObjOrProxy = null;
353             }
354         }
355
356         void referenceProcessing(Image oldImage)
357         {
358             SingleRef oldRef = (SingleRef) oldImage;
359             boolean isSame = getReferenceObjectOrProxy() == oldRef.getReferenceObjectOrProxy();
360             if(!isSame)
361             {
362                 Identity newOid = getIdentity();
363                 Identity oldOid = oldRef.getIdentity();
364                 if(newOid == null)
365                 {
366                     if(oldOid != null)
367                     {
368                         listener.deletedOneToOne(ord, oldRef.getReferenceObjectOrProxy(), oldOid, true);
369                     }
370                 }
371                 else
372                 {
373                     if(oldOid == null)
374                     {
375                         listener.addedOneToOne(ord, getReferenceObjectOrProxy(), newOid);
376                     }
377                     else
378                     {
379                         if(!newOid.equals(oldOid))
380                         {
381                             listener.deletedOneToOne(ord, oldRef.getReferenceObjectOrProxy(), oldOid, false);
382                             listener.addedOneToOne(ord, getReferenceObjectOrProxy(), newOid);
383                         }
384                     }
385                 }
386             }
387         }
388
389         public Object JavaDoc getReferenceObjectOrProxy()
390         {
391             return referenceObjOrProxy;
392         }
393
394         private Identity getIdentity()
395         {
396             if(oid == null || oid.isTransient())
397             {
398                 if(referenceObjOrProxy != null)
399                 {
400                     oid = listener.getBroker().serviceIdentity().buildIdentity(referenceObjOrProxy);
401                 }
402             }
403             return oid;
404         }
405
406         /**
407          * If a 1:1 reference has changed it will
408          * affects the main object (FK needs update).
409          */

410         public boolean modified(Image toCompare)
411         {
412             boolean modified = false;
413             if(!(this == toCompare))
414             {
415                 if(toCompare instanceof Image.SingleRef)
416                 {
417                     Image.SingleRef other = (Image.SingleRef) toCompare;
418                     Identity current = getIdentity();
419                     Identity otherOid = other.getIdentity();
420                     modified = current != null ? !current.equals(otherOid) : !(otherOid == null);
421                 }
422             }
423             return modified;
424         }
425
426         public String JavaDoc toString()
427         {
428             return ClassUtils.getShortClassName(this.getClass()) + "[reference=" + getIdentity() + "]";
429         }
430     }
431
432     //===================================================================
433
// inner class
434
//===================================================================
435
public static class Field extends Image
436     {
437         private final FieldType type;
438         private final Object JavaDoc value;
439
440         public Field(FieldType type, Object JavaDoc value)
441         {
442             this.type = type;
443             this.value = value;
444         }
445
446         public void cleanup(boolean reuse)
447         {
448         }
449
450         void referenceProcessing(Image oldImage)
451         {
452             // nothing to do
453
}
454
455         /** If a field value has changed return 'true'. */
456         public boolean modified(Image other)
457         {
458             boolean result = false;
459             if(this == other)
460             {
461                 result = true;
462             }
463             else
464             {
465                 if(other instanceof Field)
466                 {
467                     result = !type.equals(value, ((Field) other).value);
468                 }
469             }
470             return result;
471         }
472
473         public String JavaDoc toString()
474         {
475             return ClassUtils.getShortClassName(this.getClass()) + "[type=" + type + ", value=" + value + "]";
476         }
477     }
478
479     //===================================================================
480
// inner interface
481
//===================================================================
482
public static interface ImageListener
483     {
484         public void addedOneToOne(ObjectReferenceDescriptor ord, Object JavaDoc refObjOrProxy, Identity oid);
485
486         public void deletedOneToOne(ObjectReferenceDescriptor ord, Object JavaDoc refObjOrProxy, Identity oid, boolean needsUnlink);
487
488         public void addedXToN(CollectionDescriptor ord, Object JavaDoc refObjOrProxy, Identity oid);
489
490         public void deletedXToN(CollectionDescriptor ord, Object JavaDoc refObjOrProxy, Identity oid);
491
492         public PersistenceBrokerInternal getBroker();
493     }
494
495     //====================================================
496
// inner class
497
//====================================================
498

499     /**
500      * Thrown if something unexpected is happen when handling the
501      * object images for state detection.
502      */

503     public static class ImageException extends OJBRuntimeException
504     {
505         public ImageException()
506         {
507         }
508
509         public ImageException(String JavaDoc msg)
510         {
511             super(msg);
512         }
513
514         public ImageException(Throwable JavaDoc cause)
515         {
516             super(cause);
517         }
518
519         public ImageException(String JavaDoc msg, Throwable JavaDoc cause)
520         {
521             super(msg, cause);
522         }
523     }
524 }
525
Popular Tags