Coverage Report - org.apache.ojb.odmg.Image
 
Classes in this File Line Coverage Branch Coverage Complexity
Image
N/A
N/A
2.286
Image$1
N/A
N/A
2.286
Image$Field
N/A
N/A
2.286
Image$ImageException
N/A
N/A
2.286
Image$ImageListener
N/A
N/A
2.286
Image$MultipleRef
N/A
N/A
2.286
Image$SingleRef
N/A
N/A
2.286
 
 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;
 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  
  * 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 2007-08-24 22:17:37 ewestfal 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 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  
                     TODO: avoid this cast
 115  
                     e.g. change CollectionProxy interface - CollectionProxy should
 116  
                     extend Collection to support Iterator
 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  
             // find deleted reference objects
 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  
                     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 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  
                     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 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  
          * 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 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 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 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  
          * 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 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 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  
             // 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 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 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  
     // 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 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  
 }