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 //////////////////////////////////////////////////////////////////////////// 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 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 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 //////////////////////////////////////////////////////////////////////////// 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 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 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