1 | /* |
2 | * Copyright 2007 brunella ltd |
3 | * |
4 | * Licensed under the LGPL Version 3 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * |
7 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
8 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
9 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
10 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
11 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
12 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
13 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
14 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
15 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
16 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
17 | * THE POSSIBILITY OF SUCH DAMAGE. |
18 | */ |
19 | package sf.qof.util; |
20 | |
21 | import java.util.HashMap; |
22 | import java.util.Map; |
23 | import java.util.WeakHashMap; |
24 | |
25 | /** |
26 | * Caches generated classes according to a specified key. |
27 | * <code>ClassGenerationCache</code> is thread-safe. |
28 | * Classes are cached separately for each class loader. |
29 | */ |
30 | public final class ClassGenerationCache { |
31 | |
32 | private ClassGenerationCache() { } |
33 | |
34 | private static final class GENERATION_PENDING { }; |
35 | |
36 | private static final Map<ClassLoader, Map<String, Class<?>>> cache = new WeakHashMap<ClassLoader, Map<String, Class<?>>>(); |
37 | |
38 | /** |
39 | * Returns a cached class for a specified key. |
40 | * If no class can be found for the specified key null is returned and |
41 | * the key is marked for generation. If a key is marked for generation |
42 | * by another thread this method waits till the other thread completes |
43 | * generation. |
44 | * |
45 | * @param key key for the cached class |
46 | * @return the cached class or null if not found |
47 | */ |
48 | public static Class<?> getCachedClass(Class<?>... key) { |
49 | Map<String, Class<?>> classCache = getClassCache(key); |
50 | synchronized (classCache) { |
51 | Class<?> clazz = classCache.get(createMapKey(key)); |
52 | while (clazz == GENERATION_PENDING.class) { |
53 | try { |
54 | // wait till the class is generated |
55 | classCache.wait(); |
56 | } catch (InterruptedException e) { |
57 | // ignore |
58 | } |
59 | clazz = classCache.get(createMapKey(key)); |
60 | } |
61 | if (clazz == null) { |
62 | classCache.put(createMapKey(key), GENERATION_PENDING.class); |
63 | } |
64 | return clazz; |
65 | } |
66 | } |
67 | |
68 | /** |
69 | * Puts a class into the class cache specified by a key. |
70 | * If the class is null the key is removed. |
71 | * |
72 | * @param clazz the cached class |
73 | * @param key the key |
74 | */ |
75 | public static void putCachedClass(Class<?> clazz, Class<?>... key) { |
76 | Map<String, Class<?>> classCache = getClassCache(key); |
77 | synchronized (classCache) { |
78 | if (clazz == null) { |
79 | classCache.remove(createMapKey(key)); |
80 | } else { |
81 | classCache.put(createMapKey(key), clazz); |
82 | } |
83 | classCache.notifyAll(); |
84 | } |
85 | } |
86 | |
87 | private static String createMapKey(Class<?>... key) { |
88 | StringBuilder sb = new StringBuilder(); |
89 | for (Class<?> clazz : key) { |
90 | sb.append(clazz.getName()); |
91 | sb.append('%'); |
92 | } |
93 | return sb.toString(); |
94 | } |
95 | |
96 | private static Map<String, Class<?>> getClassCache(Class<?>... key) { |
97 | ClassLoader classLoader = key[0].getClassLoader(); |
98 | synchronized (cache) { |
99 | Map<String, Class<?>> classCache = cache.get(classLoader); |
100 | if (classCache == null) { |
101 | classCache = new HashMap<String, Class<?>>(); |
102 | cache.put(classLoader, classCache); |
103 | } |
104 | return classCache; |
105 | } |
106 | } |
107 | } |