Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.inject.util » [javadoc | source]
    1   /**
    2    * Copyright (C) 2006 Google Inc.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    * http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package com.opensymphony.xwork2.inject.util;
   18   
   19   import static com.opensymphony.xwork2.inject.util.ReferenceType.STRONG;
   20   
   21   import java.io.IOException;
   22   import java.io.ObjectInputStream;
   23   import java.util.concurrent.Callable;
   24   import java.util.concurrent.ConcurrentHashMap;
   25   import java.util.concurrent.ConcurrentMap;
   26   import java.util.concurrent.ExecutionException;
   27   import java.util.concurrent.Future;
   28   import java.util.concurrent.FutureTask;
   29   
   30   /**
   31    * Extends {@link ReferenceMap} to support lazy loading values by overriding
   32    * {@link #create(Object)}.
   33    *
   34    * @author crazybob@google.com (Bob Lee)
   35    */
   36   public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {
   37   
   38     private static final long serialVersionUID = 0;
   39   
   40     transient ConcurrentMap<Object, Future<V>> futures =
   41         new ConcurrentHashMap<Object, Future<V>>();
   42   
   43     transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();
   44   
   45     public ReferenceCache(ReferenceType keyReferenceType,
   46         ReferenceType valueReferenceType) {
   47       super(keyReferenceType, valueReferenceType);
   48     }
   49   
   50     /**
   51      * Equivalent to {@code new ReferenceCache(STRONG, STRONG)}.
   52      */
   53     public ReferenceCache() {
   54       super(STRONG, STRONG);
   55     }
   56   
   57     /**
   58      * Override to lazy load values. Use as an alternative to {@link
   59      * #put(Object,Object)}. Invoked by getter if value isn't already cached.
   60      * Must not return {@code null}. This method will not be called again until
   61      * the garbage collector reclaims the returned value.
   62      */
   63     protected abstract V create(K key);
   64   
   65     V internalCreate(K key) {
   66       try {
   67         FutureTask<V> futureTask = new FutureTask<V>(
   68             new CallableCreate(key));
   69   
   70         // use a reference so we get the same equality semantics.
   71         Object keyReference = referenceKey(key);
   72         Future<V> future = futures.putIfAbsent(keyReference, futureTask);
   73         if (future == null) {
   74           // winning thread.
   75           try {
   76             if (localFuture.get() != null) {
   77               throw new IllegalStateException(
   78                   "Nested creations within the same cache are not allowed.");
   79             }
   80             localFuture.set(futureTask);
   81             futureTask.run();
   82             V value = futureTask.get();
   83             putStrategy().execute(this,
   84                 keyReference, referenceValue(keyReference, value));
   85             return value;
   86           } finally {
   87             localFuture.remove();
   88             futures.remove(keyReference);
   89           }
   90         } else {
   91           // wait for winning thread.
   92           return future.get();
   93         }
   94       } catch (InterruptedException e) {
   95         throw new RuntimeException(e);
   96       } catch (ExecutionException e) {
   97         Throwable cause = e.getCause();
   98         if (cause instanceof RuntimeException) {
   99           throw (RuntimeException) cause;
  100         } else if (cause instanceof Error) {
  101           throw (Error) cause;
  102         }
  103         throw new RuntimeException(cause);
  104       }
  105     }
  106   
  107     /**
  108      * {@inheritDoc}
  109      *
  110      * If this map does not contain an entry for the given key and {@link
  111      * #create(Object)} has been overridden, this method will create a new
  112      * value, put it in the map, and return it.
  113      *
  114      * @throws NullPointerException if {@link #create(Object)} returns null.
  115      * @throws java.util.concurrent.CancellationException if the creation is
  116      *  cancelled. See {@link #cancel()}.
  117      */
  118     @SuppressWarnings("unchecked")
  119     @Override public V get(final Object key) {
  120       V value = super.get(key);
  121       return (value == null)
  122         ? internalCreate((K) key)
  123         : value;
  124     }
  125   
  126     /**
  127      * Cancels the current {@link #create(Object)}. Throws {@link
  128      * java.util.concurrent.CancellationException} to all clients currently
  129      * blocked on {@link #get(Object)}.
  130      */
  131     protected void cancel() {
  132       Future<V> future = localFuture.get();
  133       if (future == null) {
  134         throw new IllegalStateException("Not in create().");
  135       }
  136       future.cancel(false);
  137     }
  138   
  139     class CallableCreate implements Callable<V> {
  140   
  141       K key;
  142   
  143       public CallableCreate(K key) {
  144         this.key = key;
  145       }
  146   
  147       public V call() {
  148         // try one more time (a previous future could have come and gone.)
  149         V value = internalGet(key);
  150         if (value != null) {
  151           return value;
  152         }
  153   
  154         // create value.
  155         value = create(key);
  156         if (value == null) {
  157           throw new NullPointerException(
  158               "create(K) returned null for: " + key);
  159         }
  160         return value;
  161       }
  162     }
  163   
  164     /**
  165      * Returns a {@code ReferenceCache} delegating to the specified {@code
  166      * function}. The specified function must not return {@code null}.
  167      */
  168     public static <K, V> ReferenceCache<K, V> of(
  169         ReferenceType keyReferenceType,
  170         ReferenceType valueReferenceType,
  171         final Function<? super K, ? extends V> function) {
  172       ensureNotNull(function);
  173       return new ReferenceCache<K, V>(keyReferenceType, valueReferenceType) {
  174         protected V create(K key) {
  175           return function.apply(key);
  176         }
  177         private static final long serialVersionUID = 0;
  178       };
  179     }
  180   
  181     private void readObject(ObjectInputStream in) throws IOException,
  182         ClassNotFoundException {
  183       in.defaultReadObject();
  184       this.futures = new ConcurrentHashMap<Object, Future<V>>();
  185       this.localFuture = new ThreadLocal<Future<V>>();
  186     }
  187   
  188   }

Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.inject.util » [javadoc | source]