diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java')
-rw-r--r-- | propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java new file mode 100644 index 0000000..b74ba88 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.reflect; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Helper for setting properties for {@link ClassLoader}. + * <p> + * http://java.dzone.com/articles/classloaderlocal-how-avoid + * + * @author Jevgeni Kabanov + * @author scheglov_ke + * @coverage core.util + */ +@SuppressWarnings("unchecked") +public class ClassLoaderLocalMap implements Opcodes { + private static final String NAME = "GEN$$ClassLoaderProperties"; + private static final Map<Object, Object> globalMap = + Collections.synchronizedMap(new WeakHashMap<Object, Object>()); + private static Method defineMethod; + private static Method findLoadedClass; + static { + try { + defineMethod = + ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{ + String.class, + byte[].class, + int.class, + int.class}); + defineMethod.setAccessible(true); + findLoadedClass = + ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class}); + findLoadedClass.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Map + // + //////////////////////////////////////////////////////////////////////////// + public static boolean containsKey(ClassLoader cl, Object key) { + if (cl == null) { + return globalMap.containsKey(key); + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + if (!hasHolder(cl)) { + return false; + } + return getLocalMap(cl).containsKey(key); + } + } + + public static void put(ClassLoader cl, Object key, Object value) { + if (cl == null) { + globalMap.put(key, value); + return; + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + getLocalMap(cl).put(key, value); + } + } + + public static Object get(ClassLoader cl, Object key) { + if (cl == null) { + return globalMap.get(key); + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + return getLocalMap(cl).get(key); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + private static boolean hasHolder(ClassLoader cl) { + String propertiesClassName = NAME; + try { + Class<?> clazz = (Class<?>) findLoadedClass.invoke(cl, new Object[]{propertiesClassName}); + if (clazz == null) { + return false; + } + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } + return true; + } + + private static Map<Object, Object> getLocalMap(ClassLoader cl) { + String holderClassName = NAME; + Class<?> holderClass; + try { + holderClass = (Class<?>) findLoadedClass.invoke(cl, new Object[]{holderClassName}); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } + if (holderClass == null) { + byte[] classBytes = buildHolderByteCode(holderClassName); + try { + holderClass = + (Class<?>) defineMethod.invoke( + cl, + new Object[]{ + holderClassName, + classBytes, + Integer.valueOf(0), + Integer.valueOf(classBytes.length)}); + } catch (InvocationTargetException e1) { + throw new RuntimeException(e1.getTargetException()); + } catch (Throwable e1) { + throw new RuntimeException(e1); + } + } + try { + return (Map<Object, Object>) holderClass.getDeclaredField("localMap").get(null); + } catch (Throwable e1) { + throw new RuntimeException(e1); + } + } + + private static byte[] buildHolderByteCode(String holderClassName) { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, holderClassName, null, "java/lang/Object", null); + { + fv = + cw.visitField( + ACC_PUBLIC + ACC_FINAL + ACC_STATIC, + "localMap", + "Ljava/util/Map;", + null, + null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, "java/util/WeakHashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/WeakHashMap", "<init>", "()V"); + mv.visitFieldInsn(PUTSTATIC, holderClassName, "localMap", "Ljava/util/Map;"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 0); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } +} |