View Javadoc

1   package org.apache.directmemory.guava;
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.util.List;
23  import java.util.Map;
24  import java.util.concurrent.Callable;
25  import java.util.concurrent.ExecutionException;
26  import java.util.concurrent.TimeUnit;
27  
28  import org.apache.directmemory.cache.CacheService;
29  
30  import com.google.common.base.Stopwatch;
31  import com.google.common.cache.Cache;
32  import com.google.common.cache.CacheStats;
33  import com.google.common.cache.ForwardingCache;
34  import com.google.common.cache.RemovalCause;
35  import com.google.common.cache.RemovalListener;
36  import com.google.common.cache.RemovalNotification;
37  import com.google.common.collect.ImmutableMap;
38  import com.google.common.collect.Lists;
39  import com.google.common.collect.Maps;
40  
41  import static com.google.common.cache.AbstractCache.SimpleStatsCounter;
42  import static com.google.common.cache.AbstractCache.StatsCounter;
43  
44  /**
45   * @since 0.2
46   */
47  public class OffHeapCache<K, V>
48      extends ForwardingCache.SimpleForwardingCache<K, V>
49      implements RemovalListener<K, V>
50  {
51      private final CacheService<K, V> cacheService;
52  
53      private final StatsCounter statsCounter = new SimpleStatsCounter();
54  
55  
56      public OffHeapCache( CacheService<K, V> cacheService, Cache<K, V> primaryCache, ForwardingListener<K, V> listener )
57      {
58          super( primaryCache );
59          this.cacheService = cacheService;
60          listener.setDelegate( this );
61      }
62  
63      @Override
64      public V getIfPresent( Object key )
65      {
66          V result = super.getIfPresent( key );
67          if ( result == null )
68          {
69              result = retrieve( key );
70          }
71          return result;
72      }
73  
74  
75      @Override
76      public V get( final K key, final Callable<? extends V> valueLoader )
77          throws ExecutionException
78      {
79          return super.get( key, new Callable<V>()
80          {
81              @Override
82              public V call()
83                  throws Exception
84              {
85                  //Check in offHeap first
86                  V result = retrieve( key );
87  
88                  //Not found in L2 then load
89                  if ( result == null )
90                  {
91                      result = valueLoader.call();
92                  }
93                  return result;
94              }
95          } );
96      }
97  
98      @Override
99      public ImmutableMap<K, V> getAllPresent( Iterable<?> keys )
100     {
101         List<?> list = Lists.newArrayList( keys );
102         ImmutableMap<K, V> result = super.getAllPresent( list );
103 
104         //All the requested keys found then no
105         //need to check L2
106         if ( result.size() == list.size() )
107         {
108             return result;
109         }
110 
111         //Look up value from L2
112         Map<K, V> r2 = Maps.newHashMap( result );
113         for ( Object key : list )
114         {
115             if ( !result.containsKey( key ) )
116             {
117                 V val = retrieve( key );
118                 if ( val != null )
119                 {
120                     //Ideally the signature of method should have been
121                     //getAllPresent(Iterable<? extends K> keys) in that
122                     //case this cast would not have been required
123                     r2.put( (K) key, val );
124                 }
125             }
126         }
127         return ImmutableMap.copyOf( r2 );
128     }
129 
130     @Override
131     public void invalidate( Object key )
132     {
133         super.invalidate( key );
134         cacheService.free( (K) key );
135     }
136 
137     @Override
138     public void invalidateAll( Iterable<?> keys )
139     {
140         super.invalidateAll( keys );
141         for ( Object key : keys )
142         {
143             cacheService.free( (K) key );
144         }
145     }
146 
147     @Override
148     public void invalidateAll()
149     {
150         super.invalidateAll();
151 
152         for ( K key : cacheService.getMap().keySet() )
153         {
154             cacheService.free( key );
155         }
156     }
157 
158     @Override
159     public void onRemoval( RemovalNotification<K, V> notification )
160     {
161         if ( notification.getCause() == RemovalCause.SIZE )
162         {
163             cacheService.put( notification.getKey(), notification.getValue() );
164         }
165     }
166 
167     public CacheStats offHeapStats()
168     {
169         return statsCounter.snapshot();
170     }
171 
172     protected V retrieve( Object key )
173     {
174         Stopwatch watch = new Stopwatch().start();
175 
176         V value = cacheService.retrieve( (K) key );
177 
178         if ( value != null )
179         {
180             statsCounter.recordLoadSuccess( watch.elapsed( TimeUnit.NANOSECONDS ) );
181         }
182         else
183         {
184             statsCounter.recordMisses( 1 );
185         }
186 
187         return value;
188     }
189 
190 }