Home | History | Annotate | Download | only in reflect
      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