001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.jcs3.jcache.proxy;
020
021import javax.cache.Cache;
022import javax.cache.CacheManager;
023import javax.cache.configuration.CacheEntryListenerConfiguration;
024import javax.cache.configuration.Configuration;
025import javax.cache.integration.CompletionListener;
026import javax.cache.processor.EntryProcessor;
027import javax.cache.processor.EntryProcessorException;
028import javax.cache.processor.EntryProcessorResult;
029
030import org.apache.commons.jcs3.jcache.JCSCache;
031
032import java.io.Serializable;
033import java.util.Iterator;
034import java.util.Map;
035import java.util.Set;
036
037// don't use a proxy, reflection is too slow here :(
038public class ClassLoaderAwareCache<K, V> implements Cache<K, V>
039{
040    private final ClassLoader loader;
041    private final JCSCache<K, V> delegate;
042
043    public ClassLoaderAwareCache(final ClassLoader loader, final JCSCache<K, V> delegate)
044    {
045        this.loader = loader;
046        this.delegate = delegate;
047    }
048
049    private ClassLoader before(final Thread thread)
050    {
051        final ClassLoader tccl = thread.getContextClassLoader();
052        thread.setContextClassLoader(loader);
053        return tccl;
054    }
055
056    @Override
057    public V get(final K key)
058    {
059        final Thread thread = Thread.currentThread();
060        final ClassLoader loader = before(thread);
061        try
062        {
063            return delegate.get(key);
064        }
065        finally
066        {
067            thread.setContextClassLoader(loader);
068        }
069    }
070
071    @Override
072    public Map<K, V> getAll(final Set<? extends K> keys)
073    {
074        final Thread thread = Thread.currentThread();
075        final ClassLoader loader = before(thread);
076        try
077        {
078            return delegate.getAll(keys);
079        }
080        finally
081        {
082            thread.setContextClassLoader(loader);
083        }
084    }
085
086    @Override
087    public boolean containsKey(final K key)
088    {
089        final Thread thread = Thread.currentThread();
090        final ClassLoader loader = before(thread);
091        try
092        {
093            return delegate.containsKey(key);
094        }
095        finally
096        {
097            thread.setContextClassLoader(loader);
098        }
099    }
100
101    @Override
102    public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
103    {
104        final Thread thread = Thread.currentThread();
105        final ClassLoader loader = before(thread);
106        try
107        {
108            delegate.loadAll(keys, replaceExistingValues, completionListener);
109        }
110        finally
111        {
112            thread.setContextClassLoader(loader);
113        }
114    }
115
116    @Override
117    public void put(final K key, final V value)
118    {
119        final Thread thread = Thread.currentThread();
120        final ClassLoader loader = before(thread);
121        try
122        {
123            delegate.put(key, value);
124        }
125        finally
126        {
127            thread.setContextClassLoader(loader);
128        }
129    }
130
131    @Override
132    public V getAndPut(final K key, final V value)
133    {
134        final Thread thread = Thread.currentThread();
135        final ClassLoader loader = before(thread);
136        try
137        {
138            return delegate.getAndPut(key, value);
139        }
140        finally
141        {
142            thread.setContextClassLoader(loader);
143        }
144    }
145
146    @Override
147    public void putAll(final Map<? extends K, ? extends V> map)
148    {
149        final Thread thread = Thread.currentThread();
150        final ClassLoader loader = before(thread);
151        try
152        {
153            delegate.putAll(map);
154        }
155        finally
156        {
157            thread.setContextClassLoader(loader);
158        }
159    }
160
161    @Override
162    public boolean putIfAbsent(final K key, final V value)
163    {
164        final Thread thread = Thread.currentThread();
165        final ClassLoader loader = before(thread);
166        try
167        {
168            return delegate.putIfAbsent(key, value);
169        }
170        finally
171        {
172            thread.setContextClassLoader(loader);
173        }
174    }
175
176    @Override
177    public boolean remove(final K key)
178    {
179        final Thread thread = Thread.currentThread();
180        final ClassLoader loader = before(thread);
181        try
182        {
183            return delegate.remove(key);
184        }
185        finally
186        {
187            thread.setContextClassLoader(loader);
188        }
189    }
190
191    @Override
192    public boolean remove(final K key, final V oldValue)
193    {
194        final Thread thread = Thread.currentThread();
195        final ClassLoader loader = before(thread);
196        try
197        {
198            return delegate.remove(key, oldValue);
199        }
200        finally
201        {
202            thread.setContextClassLoader(loader);
203        }
204    }
205
206    @Override
207    public V getAndRemove(final K key)
208    {
209        final Thread thread = Thread.currentThread();
210        final ClassLoader loader = before(thread);
211        try
212        {
213            return delegate.getAndRemove(key);
214        }
215        finally
216        {
217            thread.setContextClassLoader(loader);
218        }
219    }
220
221    @Override
222    public boolean replace(final K key, final V oldValue, final V newValue)
223    {
224        final Thread thread = Thread.currentThread();
225        final ClassLoader loader = before(thread);
226        try
227        {
228            return delegate.replace(key, oldValue, newValue);
229        }
230        finally
231        {
232            thread.setContextClassLoader(loader);
233        }
234    }
235
236    @Override
237    public boolean replace(final K key, final V value)
238    {
239        final Thread thread = Thread.currentThread();
240        final ClassLoader loader = before(thread);
241        try
242        {
243            return delegate.replace(key, value);
244        }
245        finally
246        {
247            thread.setContextClassLoader(loader);
248        }
249    }
250
251    @Override
252    public V getAndReplace(final K key, final V value)
253    {
254        final Thread thread = Thread.currentThread();
255        final ClassLoader loader = before(thread);
256        try
257        {
258            return delegate.getAndReplace(key, value);
259        }
260        finally
261        {
262            thread.setContextClassLoader(loader);
263        }
264    }
265
266    @Override
267    public void removeAll(final Set<? extends K> keys)
268    {
269        final Thread thread = Thread.currentThread();
270        final ClassLoader loader = before(thread);
271        try
272        {
273            delegate.removeAll(keys);
274        }
275        finally
276        {
277            thread.setContextClassLoader(loader);
278        }
279    }
280
281    @Override
282    public void removeAll()
283    {
284        final Thread thread = Thread.currentThread();
285        final ClassLoader loader = before(thread);
286        try
287        {
288            delegate.removeAll();
289        }
290        finally
291        {
292            thread.setContextClassLoader(loader);
293        }
294    }
295
296    @Override
297    public void clear()
298    {
299        final Thread thread = Thread.currentThread();
300        final ClassLoader loader = before(thread);
301        try
302        {
303            delegate.clear();
304        }
305        finally
306        {
307            thread.setContextClassLoader(loader);
308        }
309    }
310
311    @Override
312    public <C extends Configuration<K, V>> C getConfiguration(final Class<C> clazz)
313    {
314        final Thread thread = Thread.currentThread();
315        final ClassLoader loader = before(thread);
316        try
317        {
318            return delegate.getConfiguration(clazz);
319        }
320        finally
321        {
322            thread.setContextClassLoader(loader);
323        }
324    }
325
326    @Override
327    public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
328    {
329        final Thread thread = Thread.currentThread();
330        final ClassLoader loader = before(thread);
331        try
332        {
333            return delegate.invoke(key, entryProcessor, arguments);
334        }
335        finally
336        {
337            thread.setContextClassLoader(loader);
338        }
339    }
340
341    @Override
342    public <T> Map<K, EntryProcessorResult<T>> invokeAll(final Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments)
343    {
344        final Thread thread = Thread.currentThread();
345        final ClassLoader loader = before(thread);
346        try
347        {
348            return delegate.invokeAll(keys, entryProcessor, arguments);
349        }
350        finally
351        {
352            thread.setContextClassLoader(loader);
353        }
354    }
355
356    @Override
357    public String getName()
358    {
359        final Thread thread = Thread.currentThread();
360        final ClassLoader loader = before(thread);
361        try
362        {
363            return delegate.getName();
364        }
365        finally
366        {
367            thread.setContextClassLoader(loader);
368        }
369    }
370
371    @Override
372    public CacheManager getCacheManager()
373    {
374        final Thread thread = Thread.currentThread();
375        final ClassLoader loader = before(thread);
376        try
377        {
378            return delegate.getCacheManager();
379        }
380        finally
381        {
382            thread.setContextClassLoader(loader);
383        }
384    }
385
386    @Override
387    public void close()
388    {
389        final Thread thread = Thread.currentThread();
390        final ClassLoader loader = before(thread);
391        try
392        {
393            delegate.close();
394        }
395        finally
396        {
397            thread.setContextClassLoader(loader);
398        }
399    }
400
401    @Override
402    public boolean isClosed()
403    {
404        final Thread thread = Thread.currentThread();
405        final ClassLoader loader = before(thread);
406        try
407        {
408            return delegate.isClosed();
409        }
410        finally
411        {
412            thread.setContextClassLoader(loader);
413        }
414    }
415
416    @Override
417    public <T> T unwrap(final Class<T> clazz)
418    {
419        final Thread thread = Thread.currentThread();
420        final ClassLoader loader = before(thread);
421        try
422        {
423            return delegate.unwrap(clazz);
424        }
425        finally
426        {
427            thread.setContextClassLoader(loader);
428        }
429    }
430
431    @Override
432    public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
433    {
434        final Thread thread = Thread.currentThread();
435        final ClassLoader loader = before(thread);
436        try
437        {
438            delegate.registerCacheEntryListener(cacheEntryListenerConfiguration);
439        }
440        finally
441        {
442            thread.setContextClassLoader(loader);
443        }
444    }
445
446    @Override
447    public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
448    {
449        final Thread thread = Thread.currentThread();
450        final ClassLoader loader = before(thread);
451        try
452        {
453            delegate.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
454        }
455        finally
456        {
457            thread.setContextClassLoader(loader);
458        }
459    }
460
461    @Override
462    public Iterator<Entry<K, V>> iterator()
463    {
464        final Thread thread = Thread.currentThread();
465        final ClassLoader loader = before(thread);
466        try
467        {
468            return delegate.iterator();
469        }
470        finally
471        {
472            thread.setContextClassLoader(loader);
473        }
474    }
475
476    @Override
477    public boolean equals(final Object obj)
478    {
479        if (ClassLoaderAwareCache.class.isInstance(obj))
480        {
481            return delegate.equals(ClassLoaderAwareCache.class.cast(obj).delegate);
482        }
483        return super.equals(obj);
484    }
485
486    @Override
487    public int hashCode()
488    {
489        return delegate.hashCode();
490    }
491
492    public static <K extends Serializable, V extends Serializable> Cache<K, V> wrap(final ClassLoader loader, final JCSCache<K, V> delegate)
493    {
494        ClassLoader dontWrapLoader = ClassLoaderAwareCache.class.getClassLoader();
495        while (dontWrapLoader != null)
496        {
497            if (loader == dontWrapLoader)
498            {
499                return delegate;
500            }
501            dontWrapLoader = dontWrapLoader.getParent();
502        }
503        return new ClassLoaderAwareCache<>(loader, delegate);
504    }
505
506    public static <K extends Serializable, V extends Serializable> JCSCache<K, V> getDelegate(final Cache<?, ?> cache)
507    {
508        if (JCSCache.class.isInstance(cache))
509        {
510            return (JCSCache<K, V>) cache;
511        }
512        return ((ClassLoaderAwareCache<K, V>) cache).delegate;
513    }
514}