Save This Page
Home » xwork-2.1.5 » 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;
   24   
   25   /**
   26    * Extends {@link ReferenceMap} to support lazy loading values by overriding
   27    * {@link #create(Object)}.
   28    *
   29    * @author crazybob@google.com (Bob Lee)
   30    */
   31   public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {
   32   
   33     private static final long serialVersionUID = 0;
   34   
   35     transient ConcurrentMap<Object, Future<V>> futures =
   36         new ConcurrentHashMap<Object, Future<V>>();
   37   
   38     transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();
   39   
   40     public ReferenceCache(ReferenceType keyReferenceType,
   41         ReferenceType valueReferenceType) {
   42       super(keyReferenceType, valueReferenceType);
   43     }
   44   
   45     /**
   46      * Equivalent to {@code new ReferenceCache(STRONG, STRONG)}.
   47      */
   48     public ReferenceCache() {
   49       super(STRONG, STRONG);
   50     }
   51   
   52     /**
   53      * Override to lazy load values. Use as an alternative to {@link
   54      * #put(Object,Object)}. Invoked by getter if value isn't already cached.
   55      * Must not return {@code null}. This method will not be called again until
   56      * the garbage collector reclaims the returned value.
   57      */
   58     protected abstract V create(K key);
   59   
   60     V internalCreate(K key) {
   61       try {
   62         FutureTask<V> futureTask = new FutureTask<V>(
   63             new CallableCreate(key));
   64   
   65         // use a reference so we get the same equality semantics.
   66         Object keyReference = referenceKey(key);
   67         Future<V> future = futures.putIfAbsent(keyReference, futureTask);
   68         if (future == null) {
   69           // winning thread.
   70           try {
   71             if (localFuture.get() != null) {
   72               throw new IllegalStateException(
   73                   "Nested creations within the same cache are not allowed.");
   74             }
   75             localFuture.set(futureTask);
   76             futureTask.run();
   77             V value = futureTask.get();
   78             putStrategy().execute(this,
   79                 keyReference, referenceValue(keyReference, value));
   80             return value;
   81           } finally {
   82             localFuture.remove();
   83             futures.remove(keyReference);
   84           }
   85         } else {
   86           // wait for winning thread.
   87           return future.get();
   88         }
   89       } catch (InterruptedException e) {
   90         throw new RuntimeException(e);
   91       } catch (ExecutionException e) {
   92         Throwable cause = e.getCause();
   93         if (cause instanceof RuntimeException) {
   94           throw (RuntimeException) cause;
   95         } else if (cause instanceof Error) {
   96           throw (Error) cause;
   97         }
   98         throw new RuntimeException(cause);
   99       }
  100     }
  101   
  102     /**
  103      * {@inheritDoc}
  104      *
  105      * If this map does not contain an entry for the given key and {@link
  106      * #create(Object)} has been overridden, this method will create a new
  107      * value, put it in the map, and return it.
  108      *
  109      * @throws NullPointerException if {@link #create(Object)} returns null.
  110      * @throws java.util.concurrent.CancellationException if the creation is
  111      *  cancelled. See {@link #cancel()}.
  112      */
  113     @SuppressWarnings("unchecked")
  114     @Override public V get(final Object key) {
  115       V value = super.get(key);
  116       return (value == null)
  117         ? internalCreate((K) key)
  118         : value;
  119     }
  120   
  121     /**
  122      * Cancels the current {@link #create(Object)}. Throws {@link
  123      * java.util.concurrent.CancellationException} to all clients currently
  124      * blocked on {@link #get(Object)}.
  125      */
  126     protected void cancel() {
  127       Future<V> future = localFuture.get();
  128       if (future == null) {
  129         throw new IllegalStateException("Not in create().");
  130       }
  131       future.cancel(false);
  132     }
  133   
  134     class CallableCreate implements Callable<V> {
  135   
  136       K key;
  137   
  138       public CallableCreate(K key) {
  139         this.key = key;
  140       }
  141   
  142       public V call() {
  143         // try one more time (a previous future could have come and gone.)
  144         V value = internalGet(key);
  145         if (value != null) {
  146           return value;
  147         }
  148   
  149         // create value.
  150         value = create(key);
  151         if (value == null) {
  152           throw new NullPointerException(
  153               "create(K) returned null for: " + key);
  154         }
  155         return value;
  156       }
  157     }
  158   
  159     /**
  160      * Returns a {@code ReferenceCache} delegating to the specified {@code
  161      * function}. The specified function must not return {@code null}.
  162      */
  163     public static <K, V> ReferenceCache<K, V> of(
  164         ReferenceType keyReferenceType,
  165         ReferenceType valueReferenceType,
  166         final Function<? super K, ? extends V> function) {
  167       ensureNotNull(function);
  168       return new ReferenceCache<K, V>(keyReferenceType, valueReferenceType) {
  169         @Override
  170         protected V create(K key) {
  171           return function.apply(key);
  172         }
  173         private static final long serialVersionUID = 0;
  174       };
  175     }
  176   
  177     private void readObject(ObjectInputStream in) throws IOException,
  178         ClassNotFoundException {
  179       in.defaultReadObject();
  180       this.futures = new ConcurrentHashMap<Object, Future<V>>();
  181       this.localFuture = new ThreadLocal<Future<V>>();
  182     }
  183   
  184   }

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