blob: b74ba88691f7024a1723f59f53bf0369398a2320 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}