Coverage Report - org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ObjectCacheTwoLevelImpl
N/A
N/A
2.548
ObjectCacheTwoLevelImpl$CacheEntry
N/A
N/A
2.548
ObjectCacheTwoLevelImpl$CopyStrategy
N/A
N/A
2.548
ObjectCacheTwoLevelImpl$CopyStrategyImpl
N/A
N/A
2.548
 
 1  
 package org.apache.ojb.broker.cache;
 2  
 
 3  
 /* Copyright 2004-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.io.Serializable;
 19  
 import java.lang.ref.ReferenceQueue;
 20  
 import java.lang.ref.SoftReference;
 21  
 import java.util.HashMap;
 22  
 import java.util.Iterator;
 23  
 import java.util.Properties;
 24  
 
 25  
 import org.apache.ojb.broker.Identity;
 26  
 import org.apache.ojb.broker.PBStateEvent;
 27  
 import org.apache.ojb.broker.PBStateListener;
 28  
 import org.apache.ojb.broker.PersistenceBroker;
 29  
 import org.apache.ojb.broker.core.DelegatingPersistenceBroker;
 30  
 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
 31  
 import org.apache.ojb.broker.core.proxy.ProxyHelper;
 32  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 33  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 34  
 import org.apache.ojb.broker.metadata.MetadataException;
 35  
 import org.apache.ojb.broker.util.ClassHelper;
 36  
 import org.apache.ojb.broker.util.logging.Logger;
 37  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 38  
 import org.apache.commons.lang.builder.ToStringBuilder;
 39  
 
 40  
 /**
 41  
  * A two-level {@link ObjectCache} implementation with a session- and an application cache. The application
 42  
  * cache could be specified by the property <code>applicationCache</code>.
 43  
  * <p/>
 44  
  * The first level is a transactional session
 45  
  * cache which cache objects till {@link org.apache.ojb.broker.PersistenceBroker#close()} or if
 46  
  * a PB-tx is running till {@link org.apache.ojb.broker.PersistenceBroker#abortTransaction()} or
 47  
  * {@link org.apache.ojb.broker.PersistenceBroker#commitTransaction()}. On commit all objects written to
 48  
  * database will be pushed to the application cache.
 49  
  * </p>
 50  
  * <p/>
 51  
  * The session cache act as a temporary storage for all read/store operations of persistent objects
 52  
  * and only on commit or close of the used PB instance the buffered objects of type
 53  
  * {@link #TYPE_WRITE} will be written to the application cache. Except objects of type
 54  
  * {@link #TYPE_NEW_MATERIALIZED} these objects will be immediatly pushed to application cache.
 55  
  * </p>
 56  
  * <p/>
 57  
  * <p/>
 58  
  * </p>
 59  
  * <p/>
 60  
  * The application cache
 61  
  * </p>
 62  
  * <p/>
 63  
  * <table cellspacing="2" cellpadding="2" border="3" frame="box">
 64  
  * <tr>
 65  
  * <td><strong>Property Key</strong></td>
 66  
  * <td><strong>Property Values</strong></td>
 67  
  * </tr>
 68  
  * <p/>
 69  
  * <tr>
 70  
  * <td>applicationCache</td>
 71  
  * <td>
 72  
  * Specifies the {@link ObjectCache} implementation used as application cache (second level cache).
 73  
  * By default {@link ObjectCacheDefaultImpl} was used. It's recommended to use a shared cache implementation
 74  
  * (all used PB instances should access the same pool of objects - e.g. by using a static Map in cache
 75  
  * implementation).
 76  
  * </td>
 77  
  * </tr>
 78  
  * <p/>
 79  
  * <tr>
 80  
  * <td>copyStrategy</td>
 81  
  * <td>
 82  
  * Specifies the implementation class of the {@link ObjectCacheTwoLevelImpl.CopyStrategy}
 83  
  * interface, which was used to copy objects on read and write to application cache. If not
 84  
  * specified a default implementation based was used ({@link ObjectCacheTwoLevelImpl.CopyStrategyImpl}
 85  
  * make field-descriptor based copies of the cached objects).
 86  
  * </td>
 87  
  * </tr>
 88  
  * <p/>
 89  
  * <tr>
 90  
  * <td>forceProxies</td>
 91  
  * <td>
 92  
  * If <em>true</em> on materialization of cached objects, all referenced objects will
 93  
  * be represented by proxy objects (independent from the proxy settings in reference- or
 94  
  * collection-descriptor).
 95  
  * <br/>
 96  
  * <strong>Note:</strong> To use this feature all persistence capable objects have to be
 97  
  * interface based <strong>or</strong> the <em>ProxyFactory</em> and
 98  
  * <em>IndirectionHandler</em> implementation classes supporting dynamic proxy enhancement
 99  
  * for all classes (see OJB.properties file).
 100  
  * </td>
 101  
  * </tr>
 102  
  * </table>
 103  
  * <p/>
 104  
  *
 105  
  * @version $Id: ObjectCacheTwoLevelImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
 106  
  */
 107  
 public class ObjectCacheTwoLevelImpl implements ObjectCacheInternal, PBStateListener
 108  
 {
 109  
     private Logger log = LoggerFactory.getLogger(ObjectCacheTwoLevelImpl.class);
 110  
 
 111  
     public static final String APPLICATION_CACHE_PROP = "applicationCache";
 112  
     public static final String COPY_STRATEGY_PROP = "copyStrategy";
 113  
     public static final String FORCE_PROXIES = "forceProxies";
 114  
     private static final String DEF_COPY_STRATEGY = ObjectCacheTwoLevelImpl.CopyStrategyImpl.class.getName();
 115  
     private static final String DEF_APP_CACHE = ObjectCacheDefaultImpl.class.getName();
 116  
 
 117  
     private HashMap sessionCache;
 118  
     // private boolean enabledReadCache;
 119  
     private int invokeCounter;
 120  
     private ReferenceQueue queue = new ReferenceQueue();
 121  
     private ObjectCacheInternal applicationCache;
 122  
     private CopyStrategy copyStrategy;
 123  
     private PersistenceBrokerImpl broker;
 124  
     private boolean forceProxies = false;
 125  
 
 126  
     public ObjectCacheTwoLevelImpl(final PersistenceBroker broker, Properties prop)
 127  
     {
 128  
         // TODO: Fix cast. Cast is needed to get access to ReferenceBroker class in PBImpl, see method #lookup
 129  
         if(broker instanceof PersistenceBrokerImpl)
 130  
         {
 131  
             this.broker = (PersistenceBrokerImpl) broker;
 132  
         }
 133  
         else if(broker instanceof DelegatingPersistenceBroker)
 134  
         {
 135  
             this.broker = (PersistenceBrokerImpl) ((DelegatingPersistenceBroker) broker).getInnermostDelegate();
 136  
         }
 137  
         else
 138  
         {
 139  
             throw new RuntimeCacheException("Can't initialize two level cache, expect instance of"
 140  
                     + PersistenceBrokerImpl.class + " or of " + DelegatingPersistenceBroker.class
 141  
                     + " to setup application cache, but was " + broker);
 142  
         }
 143  
         this.sessionCache = new HashMap(100);
 144  
         // this.enabledReadCache = false;
 145  
         setupApplicationCache(this.broker, prop);
 146  
         // we add this instance as a permanent PBStateListener
 147  
         broker.addListener(this, true);
 148  
     }
 149  
 
 150  
     /**
 151  
      * Returns the {@link org.apache.ojb.broker.PersistenceBroker} instance associated with
 152  
      * this cache instance.
 153  
      */
 154  
     public PersistenceBrokerImpl getBroker()
 155  
     {
 156  
         return broker;
 157  
     }
 158  
 
 159  
     private void setupApplicationCache(PersistenceBrokerImpl broker, Properties prop)
 160  
     {
 161  
         if(log.isDebugEnabled()) log.debug("Start setup application cache for broker " + broker);
 162  
         if(prop == null)
 163  
         {
 164  
             prop = new Properties();
 165  
         }
 166  
         String copyStrategyName = prop.getProperty(COPY_STRATEGY_PROP, DEF_COPY_STRATEGY).trim();
 167  
         if(copyStrategyName.length() == 0)
 168  
         {
 169  
             copyStrategyName = DEF_COPY_STRATEGY;
 170  
         }
 171  
         String applicationCacheName = prop.getProperty(APPLICATION_CACHE_PROP, DEF_APP_CACHE).trim();
 172  
         if(applicationCacheName.length() == 0)
 173  
         {
 174  
             applicationCacheName = DEF_APP_CACHE;
 175  
         }
 176  
         
 177  
         String forceProxyValue = prop.getProperty(FORCE_PROXIES, "false").trim();
 178  
         forceProxies = Boolean.valueOf(forceProxyValue).booleanValue();
 179  
         
 180  
         if (forceProxies && broker.getProxyFactory().interfaceRequiredForProxyGeneration()){
 181  
             log.warn("'" + FORCE_PROXIES + "' is set to true, however a ProxyFactory implementation " +
 182  
                     "[" + broker.getProxyFactory().getClass().getName() +"] " +
 183  
                     " that requires persistent objects to implement an inteface is being used. Please ensure " +
 184  
                     "that all persistent objects implement an interface, or change the ProxyFactory setting to a dynamic " +
 185  
                     "proxy generator (like ProxyFactoryCGLIBImpl).");
 186  
         }
 187  
         
 188  
         Class[] type = new Class[]{PersistenceBroker.class, Properties.class};
 189  
         Object[] objects = new Object[]{broker, prop};
 190  
         try
 191  
         {
 192  
             this.copyStrategy = (CopyStrategy) ClassHelper.newInstance(copyStrategyName);
 193  
             Class target = ClassHelper.getClass(applicationCacheName);
 194  
             if(target.equals(ObjectCacheDefaultImpl.class))
 195  
             {
 196  
                 // this property doesn't make sense in context of two-level cache
 197  
                 prop.setProperty(ObjectCacheDefaultImpl.AUTOSYNC_PROP, "false");
 198  
             }
 199  
             ObjectCache temp = (ObjectCache) ClassHelper.newInstance(target, type, objects);
 200  
             if(!(temp instanceof ObjectCacheInternal))
 201  
             {
 202  
                 log.warn("Specified application cache class doesn't implement '" + ObjectCacheInternal.class.getName()
 203  
                     + "'. For best interaction only specify caches implementing the internal object cache interface.");
 204  
                 temp = new CacheDistributor.ObjectCacheInternalWrapper(temp);
 205  
             }
 206  
             this.applicationCache = (ObjectCacheInternal) temp;
 207  
         }
 208  
         catch(Exception e)
 209  
         {
 210  
             throw new MetadataException("Can't setup application cache. Specified application cache was '"
 211  
                     + applicationCacheName + "', copy strategy was '" + copyStrategyName + "'", e);
 212  
         }
 213  
         if(log.isEnabledFor(Logger.INFO))
 214  
         {
 215  
             ToStringBuilder buf = new ToStringBuilder(this);
 216  
             buf.append("copyStrategy", copyStrategyName)
 217  
                     .append("applicationCache", applicationCacheName);
 218  
             log.info("Setup cache: " + buf.toString());
 219  
         }
 220  
     }
 221  
 
 222  
     /**
 223  
      * Returns the application cache that this 2-level cache uses.
 224  
      * 
 225  
      * @return The application cache
 226  
      */
 227  
     public ObjectCacheInternal getApplicationCache()
 228  
     {
 229  
         return applicationCache;
 230  
     }
 231  
 
 232  
     private Object lookupFromApplicationCache(Identity oid)
 233  
     {
 234  
         Object result = null;
 235  
         Object obj = getApplicationCache().lookup(oid);
 236  
         if(obj != null)
 237  
         {
 238  
             result = copyStrategy.read(broker, obj);
 239  
         }
 240  
         return result;
 241  
     }
 242  
 
 243  
     private boolean putToApplicationCache(Identity oid, Object obj, boolean cacheIfNew)
 244  
     {
 245  
         /*
 246  
         we allow to reuse cached objects, so lookup the old cache object
 247  
         and forward it to the CopyStrategy
 248  
         */
 249  
         Object oldTarget = null;
 250  
         if(!cacheIfNew)
 251  
         {
 252  
             oldTarget = getApplicationCache().lookup(oid);
 253  
         }
 254  
         Object target = copyStrategy.write(broker, obj, oldTarget);
 255  
         if(cacheIfNew)
 256  
         {
 257  
             return getApplicationCache().cacheIfNew(oid, target);
 258  
         }
 259  
         else
 260  
         {
 261  
             getApplicationCache().cache(oid, target);
 262  
             return false;
 263  
         }
 264  
     }
 265  
 
 266  
     /**
 267  
      * Discard all session cached objects and reset the state of
 268  
      * this class for further usage.
 269  
      */
 270  
     public void resetSessionCache()
 271  
     {
 272  
         sessionCache.clear();
 273  
         invokeCounter = 0;
 274  
     }
 275  
 
 276  
     /**
 277  
      * Push all cached objects of the specified type, e.g. like {@link #TYPE_WRITE} to
 278  
      * the application cache and reset type to the specified one.
 279  
      */
 280  
     private void pushToApplicationCache(int typeToProcess, int typeAfterProcess)
 281  
     {
 282  
         for(Iterator iter = sessionCache.values().iterator(); iter.hasNext();)
 283  
         {
 284  
             CacheEntry entry = (CacheEntry) iter.next();
 285  
             // if the cached object was garbage collected, nothing to do
 286  
             Object result = entry.get();
 287  
             if(result == null)
 288  
             {
 289  
                 if(log.isDebugEnabled())
 290  
                     log.debug("Object in session cache was gc, nothing to push to application cache");
 291  
             }
 292  
             else
 293  
             {
 294  
                 // push all objects of the specified type to application cache
 295  
                 if(entry.type == typeToProcess)
 296  
                 {
 297  
                     if(log.isDebugEnabled())
 298  
                     {
 299  
                         log.debug("Move obj from session cache --> application cache : " + entry.oid);
 300  
                     }
 301  
                     /*
 302  
                     arminw:
 303  
                     only cache non-proxy or real subject of materialized proxy objects
 304  
                     */
 305  
                     if(ProxyHelper.isMaterialized(result))
 306  
                     {
 307  
                         putToApplicationCache(entry.oid, ProxyHelper.getRealObject(result), false);
 308  
                         // set the new type after the object was pushed to application cache
 309  
                         entry.type = typeAfterProcess;
 310  
                     }
 311  
                 }
 312  
             }
 313  
         }
 314  
     }
 315  
 
 316  
     /**
 317  
      * Cache the given object. Creates a
 318  
      * {@link org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl.CacheEntry} and put it
 319  
      * to session cache. If the specified object to cache is of type {@link #TYPE_NEW_MATERIALIZED}
 320  
      * it will be immediately pushed to the application cache.
 321  
      */
 322  
     public void doInternalCache(Identity oid, Object obj, int type)
 323  
     {
 324  
         processQueue();
 325  
         // pass new materialized objects immediately to application cache
 326  
         if(type == TYPE_NEW_MATERIALIZED)
 327  
         {
 328  
             boolean result = putToApplicationCache(oid, obj, true);
 329  
             CacheEntry entry = new CacheEntry(oid, obj, TYPE_CACHED_READ, queue);
 330  
             if(result)
 331  
             {
 332  
                 // as current session says this object is new, put it
 333  
                 // in session cache
 334  
                 putToSessionCache(oid, entry, false);
 335  
             }
 336  
             else
 337  
             {
 338  
                 // object is not new, but if not in session cache
 339  
                 // put it in
 340  
                 putToSessionCache(oid, entry, true);
 341  
                 if(log.isDebugEnabled())
 342  
                 {
 343  
                     log.debug("The 'new' materialized object was already in cache," +
 344  
                             " will not push it to application cache: " + oid);
 345  
                 }
 346  
             }
 347  
         }
 348  
         else
 349  
         {
 350  
             // other types of cached objects will only be put to the session
 351  
             // cache.
 352  
             CacheEntry entry = new CacheEntry(oid, obj, type, queue);
 353  
             putToSessionCache(oid, entry, false);
 354  
         }
 355  
     }
 356  
 
 357  
     /**
 358  
      * Lookup corresponding object from session cache or if not found from
 359  
      * the underlying real {@link ObjectCache} - Return <em>null</em> if no
 360  
      * object was found.
 361  
      */
 362  
     public Object lookup(Identity oid)
 363  
     {
 364  
         Object result = null;
 365  
         // 1. lookup an instance in session cache
 366  
         CacheEntry entry = (CacheEntry) sessionCache.get(oid);
 367  
         if(entry != null)
 368  
         {
 369  
             result = entry.get();
 370  
         }
 371  
         if(result == null)
 372  
         {
 373  
             result = lookupFromApplicationCache(oid);
 374  
             // 4. if we have a match
 375  
             // put object in session cache
 376  
             if(result != null)
 377  
             {
 378  
                 doInternalCache(oid, result, TYPE_CACHED_READ);
 379  
                 materializeFullObject(result);
 380  
                 if(log.isDebugEnabled()) log.debug("Materialized object from second level cache: " + oid);
 381  
             }
 382  
         }
 383  
         if(result != null && log.isDebugEnabled())
 384  
         {
 385  
             log.debug("Match for: " + oid);
 386  
         }
 387  
         return result;
 388  
     }
 389  
 
 390  
     /**
 391  
      * This cache implementation cache only "flat" objects (persistent objects without any
 392  
      * references), so when {@link #lookup(org.apache.ojb.broker.Identity)} a cache object
 393  
      * it needs full materialization (assign all referenced objects) before the cache returns
 394  
      * the object. The materialization of the referenced objects based on the auto-XXX settings
 395  
      * specified in the metadata mapping.
 396  
      * <br/>
 397  
      * Override this method if needed in conjunction with a user-defined
 398  
      * {@link org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl.CopyStrategy}.
 399  
      *
 400  
      * @param target The "flat" object for full materialization
 401  
      */
 402  
     public void materializeFullObject(Object target)
 403  
     {
 404  
         ClassDescriptor cld = broker.getClassDescriptor(target.getClass());
 405  
         // don't force, let OJB use the user settings
 406  
         final boolean forced = false;
 407  
         if (forceProxies){
 408  
             broker.getReferenceBroker().retrieveProxyReferences(target, cld, forced);
 409  
             broker.getReferenceBroker().retrieveProxyCollections(target, cld, forced);
 410  
         }else{
 411  
             broker.getReferenceBroker().retrieveReferences(target, cld, forced);
 412  
             broker.getReferenceBroker().retrieveCollections(target, cld, forced);
 413  
         }    
 414  
     }
 415  
 
 416  
     /**
 417  
      * Remove the corresponding object from session AND application cache.
 418  
      */
 419  
     public void remove(Identity oid)
 420  
     {
 421  
         if(log.isDebugEnabled()) log.debug("Remove object " + oid);
 422  
         sessionCache.remove(oid);
 423  
         getApplicationCache().remove(oid);
 424  
     }
 425  
 
 426  
     /**
 427  
      * Clear session cache and application cache.
 428  
      */
 429  
     public void clear()
 430  
     {
 431  
         sessionCache.clear();
 432  
         getApplicationCache().clear();
 433  
     }
 434  
 
 435  
     /**
 436  
      * Put the specified object to session cache.
 437  
      */
 438  
     public void cache(Identity oid, Object obj)
 439  
     {
 440  
         doInternalCache(oid, obj, TYPE_UNKNOWN);
 441  
     }
 442  
 
 443  
     public boolean cacheIfNew(Identity oid, Object obj)
 444  
     {
 445  
         boolean result = putToApplicationCache(oid, obj, true);
 446  
         if(result)
 447  
         {
 448  
             CacheEntry entry = new CacheEntry(oid, obj, TYPE_CACHED_READ, queue);
 449  
             putToSessionCache(oid, entry, true);
 450  
         }
 451  
         return result;
 452  
     }
 453  
 
 454  
     /**
 455  
      * Put object to session cache.
 456  
      *
 457  
      * @param oid The {@link org.apache.ojb.broker.Identity} of the object to cache
 458  
      * @param entry The {@link org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl.CacheEntry} of the object
 459  
      * @param onlyIfNew Flag, if set <em>true</em> only new objects (not already in session cache) be cached.
 460  
      */
 461  
     private void putToSessionCache(Identity oid, CacheEntry entry, boolean onlyIfNew)
 462  
     {
 463  
         if(onlyIfNew)
 464  
         {
 465  
             // no synchronization needed, because session cache was used per broker instance
 466  
             if(!sessionCache.containsKey(oid)) sessionCache.put(oid, entry);
 467  
         }
 468  
         else
 469  
         {
 470  
             sessionCache.put(oid, entry);
 471  
         }
 472  
     }
 473  
 
 474  
     /**
 475  
      * Make sure that the Identity objects of garbage collected cached
 476  
      * objects are removed too.
 477  
      */
 478  
     private void processQueue()
 479  
     {
 480  
         CacheEntry sv;
 481  
         while((sv = (CacheEntry) queue.poll()) != null)
 482  
         {
 483  
             sessionCache.remove(sv.oid);
 484  
         }
 485  
     }
 486  
 
 487  
     //------------------------------------------------------------
 488  
     // PBStateListener methods
 489  
     //------------------------------------------------------------
 490  
     /**
 491  
      * After committing the transaction push the object
 492  
      * from session cache ( 1st level cache) to the application cache
 493  
      * (2d level cache). Finally, clear the session cache.
 494  
      */
 495  
     public void afterCommit(PBStateEvent event)
 496  
     {
 497  
         if(log.isDebugEnabled()) log.debug("afterCommit() call, push objects to application cache");
 498  
         if(invokeCounter != 0)
 499  
         {
 500  
             log.error("** Please check method calls of ObjectCacheTwoLevelImpl#enableMaterialization and" +
 501  
                     " ObjectCacheTwoLevelImpl#disableMaterialization, number of calls have to be equals **");
 502  
         }
 503  
         try
 504  
         {
 505  
             // we only push "really modified objects" to the application cache
 506  
             pushToApplicationCache(TYPE_WRITE, TYPE_CACHED_READ);
 507  
         }
 508  
         finally
 509  
         {
 510  
             resetSessionCache();
 511  
         }
 512  
     }
 513  
 
 514  
     /**
 515  
      * Before closing the PersistenceBroker ensure that the session
 516  
      * cache is cleared
 517  
      */
 518  
     public void beforeClose(PBStateEvent event)
 519  
     {
 520  
         /*
 521  
         arminw:
 522  
         this is a workaround for use in managed environments. When a PB instance is used
 523  
         within a container a PB.close call is done when leave the container method. This close
 524  
         the PB handle (but the real instance is still in use) and the PB listener are notified.
 525  
         But the JTA tx was not committed at
 526  
         this point in time and the session cache should not be cleared, because the updated/new
 527  
         objects will be pushed to the real cache on commit call (if we clear, nothing to push).
 528  
         So we check if the real broker is in a local tx (in this case we are in a JTA tx and the handle
 529  
         is closed), if true we don't reset the session cache.
 530  
         */
 531  
         if(!broker.isInTransaction())
 532  
         {
 533  
             if(log.isDebugEnabled()) log.debug("Clearing the session cache");
 534  
             resetSessionCache();
 535  
         }
 536  
     }
 537  
 
 538  
     /**
 539  
      * Before rollbacking clear the session cache (first level cache)
 540  
      */
 541  
     public void beforeRollback(PBStateEvent event)
 542  
     {
 543  
         if(log.isDebugEnabled()) log.debug("beforeRollback()");
 544  
         resetSessionCache();
 545  
     }
 546  
 
 547  
     public void afterOpen(PBStateEvent event)
 548  
     {
 549  
     }
 550  
 
 551  
     public void beforeBegin(PBStateEvent event)
 552  
     {
 553  
     }
 554  
 
 555  
     public void afterBegin(PBStateEvent event)
 556  
     {
 557  
     }
 558  
 
 559  
     public void beforeCommit(PBStateEvent event)
 560  
     {
 561  
     }
 562  
 
 563  
     public void afterRollback(PBStateEvent event)
 564  
     {
 565  
     }
 566  
     //------------------------------------------------------------
 567  
 
 568  
     //-----------------------------------------------------------
 569  
     // inner class
 570  
     //-----------------------------------------------------------
 571  
 
 572  
     /**
 573  
      * Helper class to wrap cached objects using {@link java.lang.ref.SoftReference}, which
 574  
      * allows to release objects when they no longer referenced within the PB session.
 575  
      */
 576  
     static final class CacheEntry extends SoftReference implements Serializable
 577  
     {
 578  
         private int type;
 579  
         private Identity oid;
 580  
 
 581  
         public CacheEntry(Identity oid, Object obj, int type, final ReferenceQueue q)
 582  
         {
 583  
             super(obj, q);
 584  
             this.oid = oid;
 585  
             this.type = type;
 586  
         }
 587  
     }
 588  
 
 589  
 
 590  
     public interface CopyStrategy
 591  
     {
 592  
         /**
 593  
          * Called when an object is read from the application cache (second level cache)
 594  
          * before the object is full materialized, see {@link ObjectCacheTwoLevelImpl#materializeFullObject(Object)}.
 595  
          *
 596  
          * @param broker The current used {@link org.apache.ojb.broker.PersistenceBroker} instance.
 597  
          * @param obj The object read from the application cache.
 598  
          * @return A copy of the object.
 599  
          */
 600  
         public Object read(PersistenceBroker broker, Object obj);
 601  
 
 602  
         /**
 603  
          * Called before an object is written to the application cache (second level cache).
 604  
          *
 605  
          * @param broker The current used {@link org.apache.ojb.broker.PersistenceBroker} instance.
 606  
          * @param obj The object to cache in application cache.
 607  
          * @param oldObject The old cache object or <em>null</em>
 608  
          * @return A copy of the object to write to application cache.
 609  
          */
 610  
         public Object write(PersistenceBroker broker, Object obj, Object oldObject);
 611  
     }
 612  
 
 613  
     public static class CopyStrategyImpl implements CopyStrategy
 614  
     {
 615  
         static final String CLASS_NAME_STR = "ojbClassName11";
 616  
 
 617  
         public CopyStrategyImpl()
 618  
         {
 619  
         }
 620  
 
 621  
         public Object read(PersistenceBroker broker, Object obj)
 622  
         {
 623  
             HashMap source = (HashMap) obj;
 624  
             String className = (String) source.get(CLASS_NAME_STR);
 625  
             ClassDescriptor cld = broker.getDescriptorRepository().getDescriptorFor(className);
 626  
             Object target = ClassHelper.buildNewObjectInstance(cld);
 627  
             // perform main object values
 628  
             FieldDescriptor[] flds = cld.getFieldDescriptor(true);
 629  
             FieldDescriptor fld;
 630  
             int length = flds.length;
 631  
             for(int i = 0; i < length; i++)
 632  
             {
 633  
                 fld = flds[i];
 634  
                 // read the field value
 635  
                 Object value = source.get(fld.getPersistentField().getName());
 636  
                 // copy the field value
 637  
                 if(value != null) value = fld.getJdbcType().getFieldType().copy(value);
 638  
                 // now make a field-conversion to java-type, because we only
 639  
                 // the sql type of the field
 640  
                 value = fld.getFieldConversion().sqlToJava(value);
 641  
                 // set the copied field value in new object
 642  
                 fld.getPersistentField().set(target, value);
 643  
             }
 644  
             return target;
 645  
         }
 646  
 
 647  
         public Object write(PersistenceBroker broker, Object obj, Object oldObject)
 648  
         {
 649  
             ClassDescriptor cld = broker.getClassDescriptor(obj.getClass());
 650  
             // we store field values by name in a Map
 651  
             HashMap target = oldObject != null ? (HashMap) oldObject : new HashMap();
 652  
             // perform main object values
 653  
             FieldDescriptor[] flds = cld.getFieldDescriptor(true);
 654  
             FieldDescriptor fld;
 655  
             int length = flds.length;
 656  
             for(int i = 0; i < length; i++)
 657  
             {
 658  
                 fld = flds[i];
 659  
                 // get the value
 660  
                 Object value = fld.getPersistentField().get(obj);
 661  
                 // convert value to a supported sql type
 662  
                 value = fld.getFieldConversion().javaToSql(value);
 663  
                 // copy the sql type
 664  
                 value = fld.getJdbcType().getFieldType().copy(value);
 665  
                 target.put(fld.getPersistentField().getName(), value);
 666  
             }
 667  
             target.put(CLASS_NAME_STR, obj.getClass().getName());
 668  
             return target;
 669  
         }
 670  
     }
 671  
 }