• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2011 Google, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *    Google, Inc. - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.wb.internal.core.utils.reflect;
12 
13 import org.objectweb.asm.ClassWriter;
14 import org.objectweb.asm.FieldVisitor;
15 import org.objectweb.asm.MethodVisitor;
16 import org.objectweb.asm.Opcodes;
17 
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.Collections;
21 import java.util.Map;
22 import java.util.WeakHashMap;
23 
24 /**
25  * Helper for setting properties for {@link ClassLoader}.
26  * <p>
27  * http://java.dzone.com/articles/classloaderlocal-how-avoid
28  *
29  * @author Jevgeni Kabanov
30  * @author scheglov_ke
31  * @coverage core.util
32  */
33 @SuppressWarnings("unchecked")
34 public class ClassLoaderLocalMap implements Opcodes {
35   private static final String NAME = "GEN$$ClassLoaderProperties";
36   private static final Map<Object, Object> globalMap =
37       Collections.synchronizedMap(new WeakHashMap<Object, Object>());
38   private static Method defineMethod;
39   private static Method findLoadedClass;
40   static {
41     try {
42       defineMethod =
43           ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{
44               String.class,
45               byte[].class,
46               int.class,
47               int.class});
48       defineMethod.setAccessible(true);
49       findLoadedClass =
50           ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class});
51       findLoadedClass.setAccessible(true);
52     } catch (NoSuchMethodException e) {
53       throw new RuntimeException(e);
54     }
55   }
56 
57   ////////////////////////////////////////////////////////////////////////////
58   //
59   // Map
60   //
61   ////////////////////////////////////////////////////////////////////////////
containsKey(ClassLoader cl, Object key)62   public static boolean containsKey(ClassLoader cl, Object key) {
63     if (cl == null) {
64       return globalMap.containsKey(key);
65     }
66     // synchronizing over ClassLoader is usually safest
67     synchronized (cl) {
68       if (!hasHolder(cl)) {
69         return false;
70       }
71       return getLocalMap(cl).containsKey(key);
72     }
73   }
74 
put(ClassLoader cl, Object key, Object value)75   public static void put(ClassLoader cl, Object key, Object value) {
76     if (cl == null) {
77       globalMap.put(key, value);
78       return;
79     }
80     // synchronizing over ClassLoader is usually safest
81     synchronized (cl) {
82       getLocalMap(cl).put(key, value);
83     }
84   }
85 
get(ClassLoader cl, Object key)86   public static Object get(ClassLoader cl, Object key) {
87     if (cl == null) {
88       return globalMap.get(key);
89     }
90     // synchronizing over ClassLoader is usually safest
91     synchronized (cl) {
92       return getLocalMap(cl).get(key);
93     }
94   }
95 
96   ////////////////////////////////////////////////////////////////////////////
97   //
98   // Implementation
99   //
100   ////////////////////////////////////////////////////////////////////////////
hasHolder(ClassLoader cl)101   private static boolean hasHolder(ClassLoader cl) {
102     String propertiesClassName = NAME;
103     try {
104       Class<?> clazz = (Class<?>) findLoadedClass.invoke(cl, new Object[]{propertiesClassName});
105       if (clazz == null) {
106         return false;
107       }
108     } catch (IllegalArgumentException e) {
109       throw new RuntimeException(e);
110     } catch (IllegalAccessException e) {
111       throw new RuntimeException(e);
112     } catch (InvocationTargetException e) {
113       throw new RuntimeException(e.getTargetException());
114     }
115     return true;
116   }
117 
getLocalMap(ClassLoader cl)118   private static Map<Object, Object> getLocalMap(ClassLoader cl) {
119     String holderClassName = NAME;
120     Class<?> holderClass;
121     try {
122       holderClass = (Class<?>) findLoadedClass.invoke(cl, new Object[]{holderClassName});
123     } catch (IllegalArgumentException e) {
124       throw new RuntimeException(e);
125     } catch (IllegalAccessException e) {
126       throw new RuntimeException(e);
127     } catch (InvocationTargetException e) {
128       throw new RuntimeException(e.getTargetException());
129     }
130     if (holderClass == null) {
131       byte[] classBytes = buildHolderByteCode(holderClassName);
132       try {
133         holderClass =
134             (Class<?>) defineMethod.invoke(
135                 cl,
136                 new Object[]{
137                     holderClassName,
138                     classBytes,
139                     Integer.valueOf(0),
140                     Integer.valueOf(classBytes.length)});
141       } catch (InvocationTargetException e1) {
142         throw new RuntimeException(e1.getTargetException());
143       } catch (Throwable e1) {
144         throw new RuntimeException(e1);
145       }
146     }
147     try {
148       return (Map<Object, Object>) holderClass.getDeclaredField("localMap").get(null);
149     } catch (Throwable e1) {
150       throw new RuntimeException(e1);
151     }
152   }
153 
buildHolderByteCode(String holderClassName)154   private static byte[] buildHolderByteCode(String holderClassName) {
155     ClassWriter cw = new ClassWriter(0);
156     FieldVisitor fv;
157     MethodVisitor mv;
158     cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, holderClassName, null, "java/lang/Object", null);
159     {
160       fv =
161           cw.visitField(
162               ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
163               "localMap",
164               "Ljava/util/Map;",
165               null,
166               null);
167       fv.visitEnd();
168     }
169     {
170       mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
171       mv.visitCode();
172       mv.visitTypeInsn(NEW, "java/util/WeakHashMap");
173       mv.visitInsn(DUP);
174       mv.visitMethodInsn(INVOKESPECIAL, "java/util/WeakHashMap", "<init>", "()V");
175       mv.visitFieldInsn(PUTSTATIC, holderClassName, "localMap", "Ljava/util/Map;");
176       mv.visitInsn(RETURN);
177       mv.visitMaxs(2, 0);
178       mv.visitEnd();
179     }
180     {
181       mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
182       mv.visitCode();
183       mv.visitVarInsn(ALOAD, 0);
184       mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
185       mv.visitInsn(RETURN);
186       mv.visitMaxs(1, 1);
187       mv.visitEnd();
188     }
189     cw.visitEnd();
190     return cw.toByteArray();
191   }
192 }
193