| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.openejb.util.proxy; |
| |
| import org.apache.openejb.util.Debug; |
| import org.apache.xbean.asm.ClassWriter; |
| import org.apache.xbean.asm.Label; |
| import org.apache.xbean.asm.MethodVisitor; |
| import org.apache.xbean.asm.Opcodes; |
| import org.apache.xbean.asm.Type; |
| |
| import javax.ejb.EJBException; |
| import java.io.Serializable; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.security.ProtectionDomain; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class LocalBeanProxyFactory implements Opcodes { |
| |
| public static final java.lang.reflect.InvocationHandler NON_BUSINESS_HANDLER = new NonBusinessHandler(); |
| |
| private static final String BUSSINESS_HANDLER_NAME = "businessHandler"; |
| private static final String NON_BUSINESS_HANDLER_NAME = "nonBusinessHandler"; |
| |
| public static Object newProxyInstance(ClassLoader classLoader, InvocationHandler handler, Class classToSubclass, final Class... interfaces) throws IllegalArgumentException { |
| try { |
| final Class proxyClass = createProxy(classToSubclass, classLoader, interfaces); |
| return constructProxy(proxyClass, handler); |
| } catch (Throwable e) { |
| throw new InternalError(Debug.printStackTrace(e)); |
| } |
| } |
| |
| public static void setInvocationHandler(Object proxy, InvocationHandler invocationHandler) { |
| try { |
| final Field field = proxy.getClass().getDeclaredField(BUSSINESS_HANDLER_NAME); |
| field.setAccessible(true); |
| try { |
| field.set(proxy, invocationHandler); |
| } finally { |
| field.setAccessible(false); |
| } |
| } catch (NoSuchFieldException e) { |
| throw new IllegalArgumentException(e); |
| } catch (IllegalAccessException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static java.lang.reflect.InvocationHandler getInvocationHandler(Object proxy) { |
| try { |
| final Field field = proxy.getClass().getDeclaredField(BUSSINESS_HANDLER_NAME); |
| field.setAccessible(true); |
| try { |
| return (java.lang.reflect.InvocationHandler) field.get(proxy); |
| } finally { |
| field.setAccessible(false); |
| } |
| } catch (NoSuchFieldException e) { |
| throw new IllegalArgumentException(e); |
| } catch (IllegalAccessException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| public static Object constructProxy(final Class clazz, final java.lang.reflect.InvocationHandler handler) throws IllegalStateException { |
| |
| final Object instance = Unsafe.allocateInstance(clazz); |
| |
| Unsafe.setValue(getDeclaredField(clazz, BUSSINESS_HANDLER_NAME), instance, handler); |
| Unsafe.setValue(getDeclaredField(clazz, NON_BUSINESS_HANDLER_NAME), instance, NON_BUSINESS_HANDLER); |
| |
| return instance; |
| } |
| |
| private static Field getDeclaredField(final Class clazz, final String fieldName) { |
| try { |
| return clazz.getDeclaredField(fieldName); |
| } catch (NoSuchFieldException e) { |
| final String message = String.format("Proxy class does not contain expected field \"%s\": %s", fieldName, clazz.getName()); |
| throw new IllegalStateException(message, e); |
| } |
| } |
| |
| public static boolean isProxy(final Class<?> clazz) { |
| return clazz.isAnnotationPresent(Proxy.class); |
| } |
| |
| public static Class createProxy(final Class<?> classToProxy, final ClassLoader cl, final String proxyName, final Class... interfaces) { |
| final String classFileName = proxyName.replace('.', '/'); |
| |
| try { |
| return cl.loadClass(proxyName); |
| } catch (Exception e) { |
| // no-op |
| } |
| |
| synchronized (LocalBeanProxyFactory.class) { // it can be done by concurrent threads |
| try { // try it again |
| return cl.loadClass(proxyName); |
| } catch (Exception e) { |
| // no-op |
| } |
| try { |
| final byte[] proxyBytes = generateProxy(classToProxy, classFileName, interfaces); |
| return Unsafe.defineClass(classToProxy, proxyName, proxyBytes, cl); |
| } catch (Exception e) { |
| throw new InternalError(e.toString()); |
| } |
| } |
| } |
| |
| public static Class createProxy(final Class<?> classToProxy, final ClassLoader cl, final Class... interfaces) { |
| return createProxy(classToProxy, cl, classToProxy.getName() + "$LocalBeanProxy", interfaces); |
| } |
| |
| public static byte[] generateProxy(final Class<?> classToProxy, final String proxyName, final Class<?>... interfaces) throws ProxyGenerationException { |
| final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); |
| |
| final String proxyClassFileName = proxyName.replace('.', '/'); |
| final String classFileName = classToProxy.getName().replace('.', '/'); |
| |
| // push class signature |
| final String[] interfaceNames = new String[interfaces.length]; |
| for (int i = 0; i < interfaces.length; i++) { |
| final Class<?> anInterface = interfaces[i]; |
| interfaceNames[i] = anInterface.getName().replace('.','/'); |
| } |
| |
| cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyClassFileName, null, classFileName, interfaceNames); |
| cw.visitSource(classFileName + ".java", null); |
| |
| cw.visitAnnotation("L" + Proxy.class.getName().replace('.', '/') + ";", true).visitEnd(); |
| |
| // push InvocationHandler fields |
| cw.visitField(ACC_FINAL + ACC_PRIVATE, BUSSINESS_HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;", null, null).visitEnd(); |
| cw.visitField(ACC_FINAL + ACC_PRIVATE, NON_BUSINESS_HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;", null, null).visitEnd(); |
| |
| final Map<String, List<Method>> methodMap = new HashMap<String, List<Method>>(); |
| |
| getNonPrivateMethods(classToProxy, methodMap); |
| |
| for (Class<?> anInterface : interfaces) { |
| getNonPrivateMethods(anInterface, methodMap); |
| } |
| |
| // Iterate over the public methods |
| for (final Map.Entry<String, List<Method>> entry : methodMap.entrySet()) { |
| |
| for (final Method method : entry.getValue()) { |
| final String name = method.getName(); |
| |
| if (Modifier.isPublic(method.getModifiers()) |
| || (method.getParameterTypes().length == 0 && ("finalize".equals(name) |
| || "clone".equals(name)))) { |
| // forward invocations of any public methods or |
| // finalize/clone methods to businessHandler |
| processMethod(cw, method, proxyClassFileName, BUSSINESS_HANDLER_NAME); |
| } else { |
| // forward invocations of any other methods to nonBusinessHandler |
| processMethod(cw, method, proxyClassFileName, NON_BUSINESS_HANDLER_NAME); |
| } |
| } |
| } |
| |
| return cw.toByteArray(); |
| } |
| |
| private static void getNonPrivateMethods(Class<?> clazz, Map<String, List<Method>> methodMap) { |
| while (clazz != null) { |
| for (Method method : clazz.getDeclaredMethods()) { |
| final int modifiers = method.getModifiers(); |
| |
| if (Modifier.isFinal(modifiers) |
| || Modifier.isPrivate(modifiers) |
| || Modifier.isStatic(modifiers)) { |
| continue; |
| } |
| |
| List<Method> methods = methodMap.get(method.getName()); |
| if (methods == null) { |
| methods = new ArrayList<Method>(); |
| methods.add(method); |
| methodMap.put(method.getName(), methods); |
| } else { |
| if (isOverridden(methods, method)) { |
| // method is overridden in superclass, so do nothing |
| } else { |
| // method is not overridden, so add it |
| methods.add(method); |
| } |
| } |
| } |
| |
| clazz = clazz.getSuperclass(); |
| } |
| } |
| |
| private static boolean isOverridden(final List<Method> methods, final Method method) { |
| for (final Method m : methods) { |
| if (Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static void processMethod(final ClassWriter cw, final Method method, final String proxyName, final String handlerName) throws ProxyGenerationException { |
| if ("<init>".equals(method.getName())) { |
| return; |
| } |
| |
| final Class<?> returnType = method.getReturnType(); |
| final Class<?>[] parameterTypes = method.getParameterTypes(); |
| final Class<?>[] exceptionTypes = method.getExceptionTypes(); |
| final int modifiers = method.getModifiers(); |
| |
| // push the method definition |
| int modifier = 0; |
| if (Modifier.isPublic(modifiers)) { |
| modifier = ACC_PUBLIC; |
| } else if (Modifier.isProtected(modifiers)) { |
| modifier = ACC_PROTECTED; |
| } |
| |
| final MethodVisitor mv = cw.visitMethod(modifier, method.getName(), getMethodSignatureAsString(returnType, parameterTypes), null, null); |
| mv.visitCode(); |
| |
| // push try/catch block, to catch declared exceptions, and to catch java.lang.Throwable |
| final Label l0 = new Label(); |
| final Label l1 = new Label(); |
| final Label l2 = new Label(); |
| |
| if (exceptionTypes.length > 0) { |
| mv.visitTryCatchBlock(l0, l1, l2, "java/lang/reflect/InvocationTargetException"); |
| } |
| |
| // push try code |
| mv.visitLabel(l0); |
| final String classNameToOverride = method.getDeclaringClass().getName().replace('.', '/'); |
| mv.visitLdcInsn(Type.getType("L" + classNameToOverride + ";")); |
| |
| // the following code generates the bytecode for this line of Java: |
| // Method method = <proxy>.class.getMethod("add", new Class[] { <array of function argument classes> }); |
| |
| // get the method name to invoke, and push to stack |
| mv.visitLdcInsn(method.getName()); |
| |
| // create the Class[] |
| createArrayDefinition(mv, parameterTypes.length, Class.class); |
| |
| int length = 1; |
| |
| // push parameters into array |
| for (int i = 0; i < parameterTypes.length; i++) { |
| // keep copy of array on stack |
| mv.visitInsn(DUP); |
| |
| final Class<?> parameterType = parameterTypes[i]; |
| |
| // push number onto stack |
| pushIntOntoStack(mv, i); |
| |
| if (parameterType.isPrimitive()) { |
| String wrapperType = getWrapperType(parameterType); |
| mv.visitFieldInsn(GETSTATIC, wrapperType, "TYPE", "Ljava/lang/Class;"); |
| } else { |
| mv.visitLdcInsn(Type.getType(getAsmTypeAsString(parameterType, true))); |
| } |
| |
| mv.visitInsn(AASTORE); |
| |
| if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) { |
| length += 2; |
| } else { |
| length++; |
| } |
| } |
| |
| // invoke getMethod() with the method name and the array of types |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); |
| |
| // store the returned method for later |
| mv.visitVarInsn(ASTORE, length); |
| |
| // the following code generates bytecode equivalent to: |
| // return ((<returntype>) invocationHandler.invoke(this, method, new Object[] { <function arguments }))[.<primitive>Value()]; |
| |
| final Label l4 = new Label(); |
| mv.visitLabel(l4); |
| mv.visitVarInsn(ALOAD, 0); |
| |
| // get the invocationHandler field from this class |
| mv.visitFieldInsn(GETFIELD, proxyName, handlerName, "Ljava/lang/reflect/InvocationHandler;"); |
| |
| // we want to pass "this" in as the first parameter |
| mv.visitVarInsn(ALOAD, 0); |
| |
| // and the method we fetched earlier |
| mv.visitVarInsn(ALOAD, length); |
| |
| // need to construct the array of objects passed in |
| |
| // create the Object[] |
| createArrayDefinition(mv, parameterTypes.length, Object.class); |
| |
| int index = 1; |
| // push parameters into array |
| for (int i = 0; i < parameterTypes.length; i++) { |
| // keep copy of array on stack |
| mv.visitInsn(DUP); |
| |
| final Class<?> parameterType = parameterTypes[i]; |
| |
| // push number onto stack |
| pushIntOntoStack(mv, i); |
| |
| if (parameterType.isPrimitive()) { |
| String wrapperType = getWrapperType(parameterType); |
| mv.visitVarInsn(getVarInsn(parameterType), index); |
| |
| mv.visitMethodInsn(INVOKESTATIC, wrapperType, "valueOf", "(" + getPrimitiveLetter(parameterType) + ")L" + wrapperType + ";"); |
| mv.visitInsn(AASTORE); |
| |
| if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) { |
| index += 2; |
| } else { |
| index++; |
| } |
| } else { |
| mv.visitVarInsn(ALOAD, index); |
| mv.visitInsn(AASTORE); |
| index++; |
| } |
| } |
| |
| // invoke the invocationHandler |
| mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); |
| |
| // cast the result |
| mv.visitTypeInsn(CHECKCAST, getCastType(returnType)); |
| |
| if (returnType.isPrimitive() && (!Void.TYPE.equals(returnType))) { |
| // get the primitive value |
| mv.visitMethodInsn(INVOKEVIRTUAL, getWrapperType(returnType), getPrimitiveMethod(returnType), "()" + getPrimitiveLetter(returnType)); |
| } |
| |
| // push return |
| mv.visitLabel(l1); |
| if (!Void.TYPE.equals(returnType)) { |
| mv.visitInsn(getReturnInsn(returnType)); |
| } else { |
| mv.visitInsn(POP); |
| mv.visitInsn(RETURN); |
| } |
| |
| // catch InvocationTargetException |
| if (exceptionTypes.length > 0) { |
| mv.visitLabel(l2); |
| mv.visitVarInsn(ASTORE, length); |
| |
| final Label l5 = new Label(); |
| mv.visitLabel(l5); |
| |
| for (int i = 0; i < exceptionTypes.length; i++) { |
| final Class<?> exceptionType = exceptionTypes[i]; |
| |
| mv.visitLdcInsn(Type.getType("L" + exceptionType.getCanonicalName().replace('.', '/') + ";")); |
| mv.visitVarInsn(ALOAD, length); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;"); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z"); |
| |
| final Label l6 = new Label(); |
| mv.visitJumpInsn(IFEQ, l6); |
| |
| final Label l7 = new Label(); |
| mv.visitLabel(l7); |
| |
| mv.visitVarInsn(ALOAD, length); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;"); |
| mv.visitTypeInsn(CHECKCAST, exceptionType.getCanonicalName().replace('.', '/')); |
| mv.visitInsn(ATHROW); |
| mv.visitLabel(l6); |
| |
| if (i == (exceptionTypes.length - 1)) { |
| mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException"); |
| mv.visitInsn(DUP); |
| mv.visitVarInsn(ALOAD, length); |
| mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"); |
| mv.visitInsn(ATHROW); |
| } |
| } |
| } |
| |
| // finish this method |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| /** |
| * Gets the appropriate bytecode instruction for RETURN, according to what type we need to return |
| * @param type Type the needs to be returned |
| * @return The matching bytecode instruction |
| */ |
| private static int getReturnInsn(final Class<?> type) { |
| if (type.isPrimitive()) { |
| if (Integer.TYPE.equals(type)) { |
| return IRETURN; |
| } else if (Boolean.TYPE.equals(type)) { |
| return IRETURN; |
| } else if (Character.TYPE.equals(type)) { |
| return IRETURN; |
| } else if (Byte.TYPE.equals(type)) { |
| return IRETURN; |
| } else if (Short.TYPE.equals(type)) { |
| return IRETURN; |
| } else if (Float.TYPE.equals(type)) { |
| return FRETURN; |
| } else if (Long.TYPE.equals(type)) { |
| return LRETURN; |
| } else if (Double.TYPE.equals(type)) { |
| return DRETURN; |
| } |
| } |
| |
| return ARETURN; |
| } |
| |
| |
| /** |
| * Returns the appropriate bytecode instruction to load a value from a variable to the stack |
| * @param type Type to load |
| * @return Bytecode instruction to use |
| */ |
| private static int getVarInsn(final Class<?> type) { |
| if (type.isPrimitive()) { |
| if (Integer.TYPE.equals(type)) { |
| return ILOAD; |
| } else if (Boolean.TYPE.equals(type)) { |
| return ILOAD; |
| } else if (Character.TYPE.equals(type)) { |
| return ILOAD; |
| } else if (Byte.TYPE.equals(type)) { |
| return ILOAD; |
| } else if (Short.TYPE.equals(type)) { |
| return ILOAD; |
| } else if (Float.TYPE.equals(type)) { |
| return FLOAD; |
| } else if (Long.TYPE.equals(type)) { |
| return LLOAD; |
| } else if (Double.TYPE.equals(type)) { |
| return DLOAD; |
| } |
| } |
| |
| throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); |
| } |
| |
| /** |
| * Returns the name of the Java method to call to get the primitive value from an Object - e.g. intValue for java.lang.Integer |
| * @param type Type whose primitive method we want to lookup |
| * @return The name of the method to use |
| */ |
| private static String getPrimitiveMethod(final Class<?> type) { |
| if (Integer.TYPE.equals(type)) { |
| return "intValue"; |
| } else if (Boolean.TYPE.equals(type)) { |
| return "booleanValue"; |
| } else if (Character.TYPE.equals(type)) { |
| return "charValue"; |
| } else if (Byte.TYPE.equals(type)) { |
| return "byteValue"; |
| } else if (Short.TYPE.equals(type)) { |
| return "shortValue"; |
| } else if (Float.TYPE.equals(type)) { |
| return "floatValue"; |
| } else if (Long.TYPE.equals(type)) { |
| return "longValue"; |
| } else if (Double.TYPE.equals(type)) { |
| return "doubleValue"; |
| } |
| |
| throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); |
| } |
| |
| /** |
| * Gets the string to use for CHECKCAST instruction, returning the correct value for any type, including primitives and arrays |
| * @param returnType The type to cast to with CHECKCAST |
| * @return CHECKCAST parameter |
| */ |
| static String getCastType(final Class<?> returnType) { |
| if (returnType.isPrimitive()) { |
| return getWrapperType(returnType); |
| } else { |
| return getAsmTypeAsString(returnType, false); |
| } |
| } |
| |
| /** |
| * Returns the wrapper type for a primitive, e.g. java.lang.Integer for int |
| */ |
| private static String getWrapperType(final Class<?> type) { |
| if (Integer.TYPE.equals(type)) { |
| return Integer.class.getCanonicalName().replace('.', '/'); |
| } else if (Boolean.TYPE.equals(type)) { |
| return Boolean.class.getCanonicalName().replace('.', '/'); |
| } else if (Character.TYPE.equals(type)) { |
| return Character.class.getCanonicalName().replace('.', '/'); |
| } else if (Byte.TYPE.equals(type)) { |
| return Byte.class.getCanonicalName().replace('.', '/'); |
| } else if (Short.TYPE.equals(type)) { |
| return Short.class.getCanonicalName().replace('.', '/'); |
| } else if (Float.TYPE.equals(type)) { |
| return Float.class.getCanonicalName().replace('.', '/'); |
| } else if (Long.TYPE.equals(type)) { |
| return Long.class.getCanonicalName().replace('.', '/'); |
| } else if (Double.TYPE.equals(type)) { |
| return Double.class.getCanonicalName().replace('.', '/'); |
| } else if (Void.TYPE.equals(type)) { |
| return Void.class.getCanonicalName().replace('.', '/'); |
| } |
| |
| throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); |
| } |
| |
| /** |
| * Invokes the most appropriate bytecode instruction to put a number on the stack |
| */ |
| private static void pushIntOntoStack(final MethodVisitor mv, final int i) { |
| if (i == 0) { |
| mv.visitInsn(ICONST_0); |
| } else if (i == 1) { |
| mv.visitInsn(ICONST_1); |
| } else if (i == 2) { |
| mv.visitInsn(ICONST_2); |
| } else if (i == 3) { |
| mv.visitInsn(ICONST_3); |
| } else if (i == 4) { |
| mv.visitInsn(ICONST_4); |
| } else if (i == 5) { |
| mv.visitInsn(ICONST_5); |
| } else if (i > 5 && i <= 255) { |
| mv.visitIntInsn(BIPUSH, i); |
| } else { |
| mv.visitIntInsn(SIPUSH, i); |
| } |
| } |
| |
| /** |
| * pushes an array of the specified size to the method visitor. The generated bytecode will leave |
| * the new array at the top of the stack. |
| * |
| * @param mv MethodVisitor to use |
| * @param size Size of the array to create |
| * @param type Type of array to create |
| * @throws ProxyGenerationException |
| */ |
| private static void createArrayDefinition(final MethodVisitor mv, final int size, final Class<?> type) throws ProxyGenerationException { |
| // create a new array of java.lang.class (2) |
| |
| if (size < 0) { |
| throw new ProxyGenerationException("Array size cannot be less than zero"); |
| } |
| |
| pushIntOntoStack(mv, size); |
| |
| mv.visitTypeInsn(ANEWARRAY, type.getCanonicalName().replace('.', '/')); |
| } |
| |
| |
| static String getMethodSignatureAsString(final Class<?> returnType, final Class<?>[] parameterTypes) { |
| final StringBuilder builder = new StringBuilder(); |
| builder.append("("); |
| for (Class<?> parameterType : parameterTypes) { |
| builder.append(getAsmTypeAsString(parameterType, true)); |
| } |
| |
| builder.append(")"); |
| builder.append(getAsmTypeAsString(returnType, true)); |
| |
| return builder.toString(); |
| } |
| |
| /** |
| * Returns the single letter that matches the given primitive in bytecode instructions |
| */ |
| private static String getPrimitiveLetter(final Class<?> type) { |
| if (Integer.TYPE.equals(type)) { |
| return "I"; |
| } else if (Void.TYPE.equals(type)) { |
| return "V"; |
| } else if (Boolean.TYPE.equals(type)) { |
| return "Z"; |
| } else if (Character.TYPE.equals(type)) { |
| return "C"; |
| } else if (Byte.TYPE.equals(type)) { |
| return "B"; |
| } else if (Short.TYPE.equals(type)) { |
| return "S"; |
| } else if (Float.TYPE.equals(type)) { |
| return "F"; |
| } else if (Long.TYPE.equals(type)) { |
| return "J"; |
| } else if (Double.TYPE.equals(type)) { |
| return "D"; |
| } |
| |
| throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); |
| } |
| |
| /** |
| * Converts a class to a String suitable for ASM. |
| * @param parameterType Class to convert |
| * @param wrap True if a non-array object should be wrapped with L and ; - e.g. Ljava/lang/Integer; |
| * @return String to use for ASM |
| */ |
| public static String getAsmTypeAsString(final Class<?> parameterType, final boolean wrap) { |
| if (parameterType.isArray()) { |
| if (parameterType.getComponentType().isPrimitive()) { |
| final Class<?> componentType = parameterType.getComponentType(); |
| return "[" + getPrimitiveLetter(componentType); |
| } else { |
| return "[" + getAsmTypeAsString(parameterType.getComponentType(), true); |
| } |
| } else { |
| if (parameterType.isPrimitive()) { |
| return getPrimitiveLetter(parameterType); |
| } else { |
| String className = parameterType.getCanonicalName(); |
| |
| if (parameterType.isMemberClass()) { |
| int lastDot = className.lastIndexOf("."); |
| className = className.substring(0, lastDot) + "$" + className.substring(lastDot + 1); |
| } |
| |
| if (wrap) { |
| return "L" + className.replace('.', '/') + ";"; |
| } else { |
| return className.replace('.', '/'); |
| } |
| } |
| } |
| } |
| |
| static class NonBusinessHandler implements java.lang.reflect.InvocationHandler, Serializable { |
| |
| public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { |
| throw new EJBException("Calling non-public methods of a local bean without any interfaces is not allowed"); |
| } |
| |
| } |
| |
| /** |
| * The methods of this class model sun.misc.Unsafe which is used reflectively |
| */ |
| private static class Unsafe { |
| |
| // sun.misc.Unsafe |
| private static final Object unsafe; |
| private static final Method defineClass; |
| private static final Method allocateInstance; |
| private static final Method putObject; |
| private static final Method objectFieldOffset; |
| |
| static { |
| final Class<?> unsafeClass; |
| try { |
| unsafeClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { |
| public Class<?> run() { |
| try { |
| return Thread.currentThread().getContextClassLoader().loadClass("sun.misc.Unsafe"); |
| } catch (Exception e) { |
| try { |
| return ClassLoader.getSystemClassLoader().loadClass("sun.misc.Unsafe"); |
| } catch (ClassNotFoundException e1) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe", e); |
| } |
| } |
| } |
| }); |
| } catch (Exception e) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe class", e); |
| } |
| |
| unsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| try { |
| Field field = unsafeClass.getDeclaredField("theUnsafe"); |
| field.setAccessible(true); |
| return field.get(null); |
| } catch (Exception e) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe", e); |
| } |
| } |
| }); |
| allocateInstance = AccessController.doPrivileged(new PrivilegedAction<Method>() { |
| public Method run() { |
| try { |
| Method mtd = unsafeClass.getDeclaredMethod("allocateInstance", Class.class); |
| mtd.setAccessible(true); |
| return mtd; |
| } catch (Exception e) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe.allocateInstance", e); |
| } |
| } |
| }); |
| objectFieldOffset = AccessController.doPrivileged(new PrivilegedAction<Method>() { |
| public Method run() { |
| try { |
| Method mtd = unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class); |
| mtd.setAccessible(true); |
| return mtd; |
| } catch (Exception e) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe.objectFieldOffset", e); |
| } |
| } |
| }); |
| putObject = AccessController.doPrivileged(new PrivilegedAction<Method>() { |
| public Method run() { |
| try { |
| Method mtd = unsafeClass.getDeclaredMethod("putObject", Object.class, long.class, Object.class); |
| mtd.setAccessible(true); |
| return mtd; |
| } catch (Exception e) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe.putObject", e); |
| } |
| } |
| }); |
| defineClass = AccessController.doPrivileged(new PrivilegedAction<Method>() { |
| public Method run() { |
| try { |
| Method mtd = unsafeClass.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class); |
| mtd.setAccessible(true); |
| return mtd; |
| } catch (Exception e) { |
| throw new IllegalStateException("Cannot get sun.misc.Unsafe.defineClass", e); |
| } |
| } |
| }); |
| } |
| |
| private static Object allocateInstance(final Class clazz) { |
| try { |
| return allocateInstance.invoke(unsafe, clazz); |
| } catch (IllegalAccessException e) { |
| throw new IllegalStateException("Failed to allocateInstance of Proxy class " + clazz.getName(), e); |
| } catch (InvocationTargetException e) { |
| Throwable throwable = e.getTargetException() != null ? e.getTargetException() : e; |
| throw new IllegalStateException("Failed to allocateInstance of Proxy class " + clazz.getName(), throwable); |
| } |
| } |
| |
| private static void setValue(final Field field, final Object object, final Object value) { |
| final long offset; |
| try { |
| offset = (Long) objectFieldOffset.invoke(unsafe, field); |
| } catch (Exception e) { |
| throw new IllegalStateException("Failed getting offset for: field=" + field.getName() + " class=" + field.getDeclaringClass().getName(), e); |
| } |
| |
| try { |
| putObject.invoke(unsafe, object, offset, value); |
| } catch (Exception e) { |
| throw new IllegalStateException("Failed putting field=" + field.getName() + " class=" + field.getDeclaringClass().getName(), e); |
| } |
| } |
| |
| private static Class defineClass(Class<?> clsToProxy, String proxyName, byte[] proxyBytes, ClassLoader cl) throws IllegalAccessException, InvocationTargetException { |
| return (Class<?>) defineClass.invoke(unsafe, proxyName, proxyBytes, 0, proxyBytes.length, cl, clsToProxy.getProtectionDomain()); |
| } |
| } |
| |
| @Target(ElementType.TYPE) |
| @Retention(RetentionPolicy.RUNTIME) |
| private static @interface Proxy { |
| } |
| } |