Coverage report

  %line %branch
org.apache.jcs.utils.access.JCSWorker
71% 
96% 

 1  
 package org.apache.jcs.utils.access;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.Serializable;
 23  
 import java.util.HashMap;
 24  
 import java.util.Map;
 25  
 
 26  
 import org.apache.commons.logging.Log;
 27  
 import org.apache.commons.logging.LogFactory;
 28  
 import org.apache.jcs.JCS;
 29  
 import org.apache.jcs.access.exception.CacheException;
 30  
 
 31  
 /**
 32  
  * Utility class to encapsulate doing a piece of work, and caching the results
 33  
  * in JCS. Simply construct this class with the region name for the Cache and
 34  
  * keep a static reference to it instead of the JCS itself. Then make a new
 35  
  * org.apache.jcs.utils.access.AbstractJCSWorkerHelper and implement Object
 36  
  * doWork() and do the work in there, returning the object to be cached. Then
 37  
  * call .getResult() with the key and the AbstractJCSWorkerHelper to get the
 38  
  * result of the work. If the object isn't allready in the Cache,
 39  
  * AbstractJCSWorkerHelper.doWork() will get called, and the result will be put
 40  
  * into the cache. If the object is allready in cache, the cached result will be
 41  
  * returned instead.
 42  
  * <p>
 43  
  * As an added bonus, multiple JCSWorkers with the same region, and key won't do
 44  
  * the work multiple times: The first JCSWorker to get started will do the work,
 45  
  * and all subsequent workers with the same region, group, and key will wait on
 46  
  * the first one and use his resulting work instead of doing the work
 47  
  * themselves.
 48  
  * <p>
 49  
  * This is ideal when the work being done is a query to the database where the
 50  
  * results may take time to be retrieved.
 51  
  * <p>
 52  
  * For example:
 53  
  *
 54  
  * <pre>
 55  
  *      public static JCSWorker cachingWorker = new JCSWorker(&quot;example region&quot;);
 56  
  *   		public Object getSomething(Serializable aKey){
 57  
  *        JCSWorkerHelper helper = new AbstractJCSWorkerHelper(){
 58  
  *          public Object doWork(){
 59  
  *            // Do some (DB?) work here which results in a list
 60  
  *            // This only happens if the cache dosn't have a item in this region for aKey
 61  
  *            // Note this is especially useful with Hibernate, which will cache indiviual
 62  
  *            // Objects, but not entire query result sets.
 63  
  *            List results = query.list();
 64  
  *            // Whatever we return here get's cached with aKey, and future calls to
 65  
  *            // getResult() on a CachedWorker with the same region and key will return that instead.
 66  
  *            return results;
 67  
  *        };
 68  
  *        List result = worker.getResult(aKey, helper);
 69  
  *      }
 70  
  * </pre>
 71  
  *
 72  
  * This is essentially the same as doing:
 73  
  *
 74  
  * <pre>
 75  
  * JCS jcs = JCS.getInstance( &quot;exampleregion&quot; );
 76  
  * List results = (List) jcs.get( aKey );
 77  
  * if ( results != null )
 78  
  * {
 79  
  *     //do the work here
 80  
  *     results = query.list();
 81  
  *     jcs.put( aKey, results );
 82  
  * }
 83  
  * </pre>
 84  
  *
 85  
  * <p>
 86  
  * But has the added benifit of the work-load sharing; under normal
 87  
  * circumstances if multiple threads all tried to do the same query at the same
 88  
  * time, the same query would happen multiple times on the database, and the
 89  
  * resulting object would get put into JCS multiple times.
 90  
  * <p>
 91  
  * @author Travis Savo
 92  
  */
 93  1
 public class JCSWorker
 94  
 {
 95  15
     private static final Log logger = LogFactory.getLog( JCSWorker.class );
 96  
 
 97  
     private JCS cache;
 98  
 
 99  
     /**
 100  
      * Map to hold who's doing work presently.
 101  
      */
 102  8
     private static volatile Map map = new HashMap();
 103  
 
 104  
     /**
 105  
      * Region for the JCS cache.
 106  
      */
 107  
     private String region;
 108  
 
 109  
     /**
 110  
      * Constructor which takes a region for the JCS cache.
 111  
      * @param aRegion
 112  
      *            The Region to use for the JCS cache.
 113  
      */
 114  1
     public JCSWorker( final String aRegion )
 115  7
     {
 116  8
         region = aRegion;
 117  
         try
 118  
         {
 119  8
             cache = JCS.getInstance( aRegion );
 120  
         }
 121  0
         catch ( CacheException e )
 122  
         {
 123  0
             throw new RuntimeException( e.getMessage() );
 124  7
         }
 125  8
     }
 126  
 
 127  
     /**
 128  
      * Getter for the region of the JCS Cache.
 129  
      * @return The JCS region in which the result will be cached.
 130  
      */
 131  
     public String getRegion()
 132  
     {
 133  48
         return region;
 134  
     }
 135  
 
 136  
     /**
 137  
      * Gets the cached result for this region/key OR does the work and caches
 138  
      * the result, returning the result. If the result has not been cached yet,
 139  
      * this calls doWork() on the JCSWorkerHelper to do the work and cache the
 140  
      * result. This is also an opertunity to do any post processing of the
 141  
      * result in your CachedWorker implementation.
 142  
      * @param aKey
 143  
      *            The key to get/put with on the Cache.
 144  
      * @param aWorker
 145  
      *            The JCSWorkerHelper implementing Object doWork(). This gets
 146  
      *            called if the cache get misses, and the result is put into
 147  
      *            cache.
 148  
      * @return The result of doing the work, or the cached result.
 149  
      * @throws Exception
 150  
      *             Throws an exception if anything goes wrong while doing the
 151  
      *             work.
 152  
      */
 153  
     public Object getResult( Serializable aKey, JCSWorkerHelper aWorker )
 154  
         throws Exception
 155  
     {
 156  16
         return run( aKey, null, aWorker );
 157  
     }
 158  
 
 159  
     /**
 160  
      * Gets the cached result for this region/key OR does the work and caches
 161  
      * the result, returning the result. If the result has not been cached yet,
 162  
      * this calls doWork() on the JCSWorkerHelper to do the work and cache the
 163  
      * result. This is also an opertunity to do any post processing of the
 164  
      * result in your CachedWorker implementation.
 165  
      * @param aKey
 166  
      *            The key to get/put with on the Cache.
 167  
      * @param aGroup
 168  
      *            The cache group to put the result in.
 169  
      * @param aWorker
 170  
      *            The JCSWorkerHelper implementing Object doWork(). This gets
 171  
      *            called if the cache get misses, and the result is put into
 172  
      *            cache.
 173  
      * @return The result of doing the work, or the cached result.
 174  
      * @throws Exception
 175  
      *             Throws an exception if anything goes wrong while doing the
 176  
      *             work.
 177  
      */
 178  
     public Object getResult( Serializable aKey, String aGroup, JCSWorkerHelper aWorker )
 179  
         throws Exception
 180  
     {
 181  0
         return run( aKey, aGroup, aWorker );
 182  
     }
 183  
 
 184  
     /**
 185  
      * Try and get the object from the cache, and if it's not there, do the work
 186  
      * and cache it. This also ensures that only one CachedWorker is doing the
 187  
      * work and subsequent calls to a CachedWorker with identical
 188  
      * region/key/group will wait on the results of this call. It will call the
 189  
      * JCSWorkerHelper.doWork() if the cache misses, and will put the result.
 190  
      * @param aKey
 191  
      * @param aGroup
 192  
      * @param aHelper
 193  
      * @return Either the result of doing the work, or the cached result.
 194  
      * @throws Exception
 195  
      *             If something goes wrong while doing the work, throw an
 196  
      *             exception.
 197  
      */
 198  
     private Object run( Serializable aKey, String aGroup, JCSWorkerHelper aHelper )
 199  
         throws Exception
 200  
     {
 201  16
         Object result = null;
 202  
         // long start = 0;
 203  
         // long dbTime = 0;
 204  16
         JCSWorkerHelper helper = null;
 205  
 
 206  18
         synchronized ( map )
 207  
         {
 208  
             // Check to see if we allready have a thread doing this work.
 209  16
             helper = (JCSWorkerHelper) map.get( getRegion() + aKey );
 210  16
             if ( helper == null )
 211  
             {
 212  
                 // If not, add ourselves as the Worker so
 213  
                 // calls in another thread will use this worker's result
 214  16
                 map.put( getRegion() + aKey, aHelper );
 215  
             }
 216  14
         }
 217  16
         if ( helper != null )
 218  
         {
 219  0
             synchronized ( helper )
 220  
             {
 221  0
                 if ( logger.isDebugEnabled() )
 222  
                 {
 223  0
                     logger.debug( "Found a worker allready doing this work (" + getRegion() + ":" + aKey + ")." );
 224  
                 }
 225  0
                 if ( !helper.isFinished() )
 226  
                 {
 227  0
                     helper.wait();
 228  
                 }
 229  0
                 if ( logger.isDebugEnabled() )
 230  
                 {
 231  0
                     logger.debug( "Another thread finished our work for us. Using thoes results instead. ("
 232  
                         + getRegion() + ":" + aKey + ")." );
 233  
                 }
 234  0
             }
 235  
         }
 236  
         // Do the work
 237  
         try
 238  
         {
 239  16
             if ( logger.isDebugEnabled() )
 240  
             {
 241  0
                 logger.debug( getRegion() + " is doing the work." );
 242  
             }
 243  16
             result = null;
 244  
 
 245  
             // Try to get the item from the cache
 246  16
             if ( aGroup != null )
 247  
             {
 248  0
                 result = cache.getFromGroup( aKey, aGroup );
 249  0
             }
 250  
             else
 251  
             {
 252  16
                 result = cache.get( aKey );
 253  
             }
 254  
             // If the cache dosn't have it, do the work.
 255  16
             if ( result == null )
 256  
             {
 257  8
                 result = aHelper.doWork();
 258  8
                 if ( logger.isDebugEnabled() )
 259  
                 {
 260  0
                     logger.debug( "Work Done, caching: key:" + aKey + ", group:" + aGroup + ", result:" + result + "." );
 261  
                 }
 262  
                 // Stick the result of the work in the cache.
 263  8
                 if ( aGroup != null )
 264  
                 {
 265  0
                     cache.putInGroup( aKey, aGroup, result );
 266  0
                 }
 267  
                 else
 268  
                 {
 269  8
                     cache.put( aKey, result );
 270  
                 }
 271  
             }
 272  
             // return the result
 273  18
             return result;
 274  
         }
 275  
         finally
 276  
         {
 277  2
             if ( logger.isDebugEnabled() )
 278  
             {
 279  0
                 logger.debug( getRegion() + ":" + aKey + " entered finally." );
 280  
             }
 281  18
             synchronized ( map )
 282  
             {
 283  
                 // Remove ourselves as the worker.
 284  16
                 if ( helper == null )
 285  
                 {
 286  16
                     map.remove( getRegion() + aKey );
 287  
                 }
 288  18
                 synchronized ( this )
 289  
                 {
 290  16
                     aHelper.setFinished( true );
 291  
                     // Wake everyone waiting on us
 292  16
                     notifyAll();
 293  14
                 }
 294  14
             }
 295  14
         }
 296  
     }
 297  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.