1 package org.apache.ojb.odmg;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
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
44
45
46
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
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 collectionOrArray;
89 private Map references;
90 private int status;
91 private boolean hasTransientIdentity;
92 private boolean isRefreshed;
93
94 public MultipleRef(ImageListener listener, CollectionDescriptor cod, Object 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
115
116
117
118 handleReferencedObjects(((Collection) 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 it = BrokerHelper.getCollectionIterator(collectionOrArray);
133 handleReferencedObjects(it);
134 }
135 }
136 }
137
138 void handleReferencedObjects(Iterator it)
139 {
140 if(it == null) return;
141 references = new HashMap();
142 if(log.isDebugEnabled()) log.debug("Handle collection references");
143 IdentityFactory idFac = listener.getBroker().serviceIdentity();
144 Identity oid;
145 Object 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
190 if(oldRefs.references.size() > 0)
191 {
192 Iterator oldIter = oldRefs.references.entrySet().iterator();
193 while(oldIter.hasNext())
194 {
195 Map.Entry entry = (Map.Entry) oldIter.next();
196 Identity oldOid = (Identity) entry.getKey();
197
198
199
200
201 if(!isUnmaterializedProxy() && !containsReference(oldOid))
202 {
203 listener.deletedXToN(cod, entry.getValue(), oldOid);
204 }
205 }
206 }
207
208
209 if(references.size() > 0)
210 {
211 Iterator newIter = references.entrySet().iterator();
212 while(newIter.hasNext())
213 {
214 Map.Entry entry = (Map.Entry) newIter.next();
215 Identity newOid = (Identity) entry.getKey();
216
217
218
219
220 if(!oldRefs.containsReference(newOid))
221 {
222 listener.addedXToN(cod, entry.getValue(), newOid);
223 }
224 }
225 }
226 }
227
228
229
230
231
232
233
234
235 private boolean incommensurableProxies(MultipleRef oldImage)
236 {
237 boolean result = false;
238
239 if(oldImage.isUnmaterializedProxy() || isUnmaterializedProxy())
240 {
241 result = !collectionOrArray.equals(oldImage.collectionOrArray);
242 }
243 return result;
244 }
245
246 private void refreshIdentities()
247 {
248
249 if(hasTransientIdentity && references.size() > 0)
250 {
251 hasTransientIdentity = false;
252
253 List list = new ArrayList(references.keySet());
254 IdentityFactory idFac = listener.getBroker().serviceIdentity();
255 Identity oid, newOid;
256 Object 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
277
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 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
308
309 public void beforeLoading(CollectionProxyDefaultImpl colProxy)
310 {
311
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 toString()
325 {
326 return ClassUtils.getShortClassName(this.getClass()) + "[references-size="
327 + (references != null ? "" + references.size() : "undefined") + "]";
328 }
329 }
330
331
332
333
334 public static class SingleRef extends Image
335 {
336 private Object 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 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 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
408
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 toString()
427 {
428 return ClassUtils.getShortClassName(this.getClass()) + "[reference=" + getIdentity() + "]";
429 }
430 }
431
432
433
434
435 public static class Field extends Image
436 {
437 private final FieldType type;
438 private final Object value;
439
440 public Field(FieldType type, Object 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
453 }
454
455
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 toString()
474 {
475 return ClassUtils.getShortClassName(this.getClass()) + "[type=" + type + ", value=" + value + "]";
476 }
477 }
478
479
480
481
482 public static interface ImageListener
483 {
484 public void addedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid);
485
486 public void deletedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink);
487
488 public void addedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid);
489
490 public void deletedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid);
491
492 public PersistenceBrokerInternal getBroker();
493 }
494
495
496
497
498
499
500
501
502
503 public static class ImageException extends OJBRuntimeException
504 {
505 public ImageException()
506 {
507 }
508
509 public ImageException(String msg)
510 {
511 super(msg);
512 }
513
514 public ImageException(Throwable cause)
515 {
516 super(cause);
517 }
518
519 public ImageException(String msg, Throwable cause)
520 {
521 super(msg, cause);
522 }
523 }
524 }