/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.nio.intraband.proxy; import com.liferay.portal.asm.ASMUtil; import com.liferay.portal.asm.MethodNodeGenerator; import com.liferay.portal.kernel.io.Deserializer; import com.liferay.portal.kernel.io.Serializer; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.nio.intraband.Datagram; import com.liferay.portal.kernel.nio.intraband.Intraband; import com.liferay.portal.kernel.nio.intraband.RegistrationReference; import com.liferay.portal.kernel.nio.intraband.SystemDataType; import com.liferay.portal.kernel.nio.intraband.proxy.ExceptionHandler; import com.liferay.portal.kernel.nio.intraband.proxy.IntrabandProxySkeleton; import com.liferay.portal.kernel.nio.intraband.proxy.TargetLocator; import com.liferay.portal.kernel.nio.intraband.proxy.annotation.Id; import com.liferay.portal.kernel.nio.intraband.proxy.annotation.Proxy; import com.liferay.portal.kernel.nio.intraband.rpc.RPCResponse; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.FileUtil; import com.liferay.portal.kernel.util.ReflectionUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.SystemProperties; import com.liferay.portal.kernel.util.TextFormatter; import com.liferay.portal.util.PropsValues; import java.io.File; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.TableSwitchGenerator; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodNode; /** * @author Shuyang Zhou */ public class IntrabandProxyUtil { public static final String SKELETON_POSTFIX = "__IntrabandProxy__Skeleton"; public static final String STUB_POSTFIX = "__IntrabandProxy__Stub"; public static String[] getProxyMethodSignatures(Class<?> clazz) { try { Field field = clazz.getField(_PROXY_METHOD_SIGNATURES_FIELD_NAME); return (String[])field.get(null); } catch (Exception e) { return null; } } public static Class<?> getStubClass(Class<?> clazz, String skeletonId) { return getStubClass(clazz.getClassLoader(), clazz, skeletonId); } public static Class<?> getStubClass( ClassLoader classLoader, Class<?> clazz, String skeletonId) { Class<?> stubClass = loadClass(classLoader, clazz, STUB_POSTFIX); if (stubClass != null) { return stubClass; } synchronized (classLoader) { stubClass = loadClass(classLoader, clazz, STUB_POSTFIX); if (stubClass != null) { return stubClass; } validate(classLoader, clazz, false); stubClass = generateStubClass(classLoader, clazz, skeletonId); } return stubClass; } public static <T> T newStubInstance( Class<? extends T> stubClass, String id, RegistrationReference registrationReference, ExceptionHandler exceptionHandler) { try { Constructor<? extends T> constructor = stubClass.getConstructor( String.class, RegistrationReference.class, ExceptionHandler.class); return constructor.newInstance( id, registrationReference, exceptionHandler); } catch (Exception e) { throw new RuntimeException(e); } } protected static void checkField( Field[] fields, String name, Class<?> clazz, boolean isStatic) { for (Field field : fields) { if (name.equals(field.getName())) { if ((field.getType() != clazz) || (Modifier.isStatic(field.getModifiers()) != isStatic)) { throw new IllegalArgumentException( "Field " + field + " is expected to be of type " + clazz + " and " + (!isStatic ? "not " : "") + "static"); } break; } } } protected static MethodNode createProxyMethodNode( Method method, int index, String skeletonId, Type stubType) { MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator( method); // Serializer serializer = new Serializer(); methodNodeGenerator.newInstance(_SERIALIZER_TYPE); methodNodeGenerator.dup(); methodNodeGenerator.invokeSpecial( _SERIALIZER_TYPE.getInternalName(), "<init>", Type.VOID_TYPE); int serializerIndex = methodNodeGenerator.newLocal(_SERIALIZER_TYPE); methodNodeGenerator.storeLocal(serializerIndex); // serializer.writeString(skeletonId); methodNodeGenerator.loadLocal(serializerIndex); methodNodeGenerator.push(skeletonId); serializerWrite(methodNodeGenerator, _STRING_TYPE); // serializer.writeString(_id); methodNodeGenerator.loadLocal(serializerIndex); methodNodeGenerator.loadThis(); methodNodeGenerator.getField(stubType, "_id", _STRING_TYPE); serializerWrite(methodNodeGenerator, _STRING_TYPE); // serializer.writeInt(index); methodNodeGenerator.loadLocal(serializerIndex); methodNodeGenerator.push(index); serializerWrite(methodNodeGenerator, Type.INT_TYPE); // serializer.writeXXX(parameters...) Class<?>[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { methodNodeGenerator.loadLocal(serializerIndex); methodNodeGenerator.loadArg(i); serializerWrite( methodNodeGenerator, Type.getType(parameterTypes[i])); } // this._send(Serializer) / return this._syncSend(Serializer) methodNodeGenerator.loadThis(); methodNodeGenerator.loadLocal(serializerIndex); Class<?> returnClass = method.getReturnType(); if (returnClass == void.class) { // this._send(Serializer) methodNodeGenerator.invokeSpecial( stubType.getInternalName(), "_send", Type.VOID_TYPE, _SERIALIZER_TYPE); methodNodeGenerator.returnValue(); } else { // return this._syncSend(Serializer) methodNodeGenerator.invokeSpecial( stubType.getInternalName(), "_syncSend", _SERIALIZABLE_TYPE, _SERIALIZER_TYPE); Type returnType = Type.getType(returnClass); if (returnClass.isPrimitive()) { // Null check and unbox int returnValueIndex = methodNodeGenerator.newLocal( _OBJECT_TYPE); methodNodeGenerator.storeLocal(returnValueIndex); methodNodeGenerator.loadLocal(returnValueIndex); Label nullCheckLabel = new Label(); methodNodeGenerator.ifNull(nullCheckLabel); methodNodeGenerator.loadLocal(returnValueIndex); methodNodeGenerator.unbox(returnType); methodNodeGenerator.returnValue(); methodNodeGenerator.visitLabel(nullCheckLabel); ASMUtil.addDefaultReturnInsns(methodNodeGenerator, returnType); } else { if (returnClass != Object.class) { methodNodeGenerator.checkCast(returnType); } methodNodeGenerator.returnValue(); } } methodNodeGenerator.endMethod(); return methodNodeGenerator.getMethodNode(); } protected static void deserializerRead( MethodNodeGenerator methodNodeGenerator, Type type) { String owner = _DESERIALIZER_TYPE.getInternalName(); if (type.getSort() <= Type.DOUBLE) { String name = TextFormatter.format( type.getClassName(), TextFormatter.G); methodNodeGenerator.invokeVirtual(owner, "read".concat(name), type); } else if (type.equals(_STRING_TYPE)) { methodNodeGenerator.invokeVirtual( owner, "readString", _STRING_TYPE); } else { methodNodeGenerator.invokeVirtual( owner, "readObject", _SERIALIZABLE_TYPE); } } protected static MethodsBag extractMethods(Class<?> clazz) { List<Method> idMethods = new ArrayList<>(); List<Method> proxyMethods = new ArrayList<>(); List<Method> emptyMethods = new ArrayList<>(); for (Method method : ReflectionUtil.getVisibleMethods(clazz)) { Id id = method.getAnnotation(Id.class); if (id != null) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalArgumentException( "The @Id annotated method " + method + " must not be static"); } Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length > 0) { throw new IllegalArgumentException( "The @Id annotated method " + method + " must not have parameters"); } if (method.getReturnType() != String.class) { throw new IllegalArgumentException( "The @Id annotated method " + method + " must not return String"); } idMethods.add(method); continue; } Proxy proxy = method.getAnnotation(Proxy.class); if (proxy != null) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalArgumentException( "Static proxy method violation for " + method); } proxyMethods.add(method); continue; } if (Modifier.isAbstract(method.getModifiers())) { emptyMethods.add(method); } } return new MethodsBag(idMethods, proxyMethods, emptyMethods); } protected static Class<? extends IntrabandProxySkeleton> generateSkeletonClass(ClassLoader classLoader, Class<?> clazz) { Type targetType = Type.getType(clazz); String internalName = targetType.getInternalName(); ClassNode classNode = ASMUtil.loadAndRename( TemplateSkeleton.class, internalName.concat(SKELETON_POSTFIX)); classNode.access &= ~Opcodes.ACC_ABSTRACT; classNode.access |= Opcodes.ACC_PUBLIC; FieldNode proxyMethodsMappingFieldNode = ASMUtil.findFieldNode( classNode.fields, "_PROXY_METHODS_MAPPING"); proxyMethodsMappingFieldNode.access |= Opcodes.ACC_FINAL; FieldNode targetLocatorFieldNode = ASMUtil.findFieldNode( classNode.fields, "_targetLocator"); targetLocatorFieldNode.access |= Opcodes.ACC_FINAL; MethodNode doDispatchMethodNode = ASMUtil.findMethodNode( classNode.methods, "doDispatch", Type.VOID_TYPE, _REGISTRATION_REFERENCE_TYPE, _DATAGRAM_TYPE, _DESERIALIZER_TYPE); doDispatchMethodNode.access &= ~Opcodes.ACC_ABSTRACT; MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator( doDispatchMethodNode); // T target = (T)_targetLocator.getTarget() methodNodeGenerator.loadThis(); methodNodeGenerator.getField( Type.getObjectType(classNode.name), "_targetLocator", _TARGET_LOCATOR_TYPE); methodNodeGenerator.loadArg(2); deserializerRead(methodNodeGenerator, _STRING_TYPE); methodNodeGenerator.invokeInterface( _TARGET_LOCATOR_TYPE.getInternalName(), "getTarget", _OBJECT_TYPE, _STRING_TYPE); methodNodeGenerator.checkCast(targetType); int typedTargetIndex = methodNodeGenerator.newLocal(targetType); methodNodeGenerator.storeLocal(typedTargetIndex); // int index = deserializer.readInt(); methodNodeGenerator.loadArg(2); deserializerRead(methodNodeGenerator, Type.INT_TYPE); methodNodeGenerator.dup(); int indexIndex = methodNodeGenerator.newLocal(Type.INT_TYPE); methodNodeGenerator.storeLocal(indexIndex); // switch (index) MethodsBag methodsBag = extractMethods(clazz); List<Method> proxyMethods = methodsBag.proxyMethods; int[] keys = new int[proxyMethods.size()]; for (int i = 0; i < keys.length; i++) { keys[i] = i; } methodNodeGenerator.tableSwitch( keys, new SkeletonDispatchTableSwitchGenerator( methodNodeGenerator, proxyMethods, classNode.name, typedTargetIndex, indexIndex), true); methodNodeGenerator.returnValue(); methodNodeGenerator.endMethod(); rewriteGetProxyMethodSignaturesMethodNode( classNode, methodsBag.proxyMethodSignatures); return (Class<? extends IntrabandProxySkeleton>)toClass( classNode, classLoader); } protected static Class<?> generateStubClass( ClassLoader classLoader, Class<?> clazz, String skeletonId) { String internalName = Type.getInternalName(clazz); ClassNode classNode = ASMUtil.loadAndRename( clazz, internalName.concat(STUB_POSTFIX)); // Class modifiers and hierarchy classNode.access &= ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE); classNode.access |= Opcodes.ACC_PUBLIC; if (clazz.isInterface()) { List<String> interfaces = classNode.interfaces; interfaces.clear(); interfaces.add(internalName); } // Clean up MethodNodes that are going to be generated List<MethodNode> methodNodes = classNode.methods; MethodNode defaultInitMethodNode = ASMUtil.removeMethodNode( methodNodes, "<init>", Type.VOID_TYPE); ASMUtil.removeMethodNodes(methodNodes, "<init>"); ASMUtil.removeMethodNodes(methodNodes, Opcodes.ACC_ABSTRACT); ASMUtil.removeMethodNodes(methodNodes, _annotationDescriptors); // Apply template class ClassNode templateClassNode = ASMUtil.loadAndRename( TemplateStub.class, classNode.name); List<FieldNode> templateFieldNodes = templateClassNode.fields; FieldNode idFieldNode = ASMUtil.findFieldNode( templateFieldNodes, "_id"); idFieldNode.access |= Opcodes.ACC_FINAL; FieldNode intrabandFieldNode = ASMUtil.findFieldNode( templateFieldNodes, "_intraband"); intrabandFieldNode.access |= Opcodes.ACC_FINAL; FieldNode registrationReferenceFieldNode = ASMUtil.findFieldNode( templateFieldNodes, "_registrationReference"); registrationReferenceFieldNode.access |= Opcodes.ACC_FINAL; ASMUtil.addFieldNodes(classNode.fields, templateFieldNodes); List<MethodNode> templateMethodNodes = templateClassNode.methods; MethodNode templateInitMethodNode = ASMUtil.findMethodNode( templateMethodNodes, "<init>", Type.VOID_TYPE, _STRING_TYPE, _REGISTRATION_REFERENCE_TYPE, _EXCEPTION_HANDLER_TYPE); if (defaultInitMethodNode != null) { ASMUtil.mergeMethods( templateInitMethodNode, defaultInitMethodNode, templateInitMethodNode); } MethodNode defaultClinitMethodNode = ASMUtil.removeMethodNode( methodNodes, "<clinit>", Type.VOID_TYPE); if (defaultClinitMethodNode != null) { MethodNode templateClinitMethodNode = ASMUtil.findMethodNode( templateMethodNodes, "<clinit>", Type.VOID_TYPE); ASMUtil.mergeMethods( templateClinitMethodNode, defaultClinitMethodNode, templateClinitMethodNode); } methodNodes.addAll(templateMethodNodes); Type stubType = Type.getType(classNode.name); // Id methods MethodsBag methodsBag = extractMethods(clazz); for (Method idMethod : methodsBag.idMethods) { MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator( idMethod); methodNodeGenerator.loadThis(); methodNodeGenerator.getField(stubType, "_id", _STRING_TYPE); methodNodeGenerator.returnValue(); methodNodeGenerator.endMethod(); methodNodes.add(methodNodeGenerator.getMethodNode()); } // Proxy methods List<Method> proxyMethods = methodsBag.proxyMethods; for (int i = 0; i < proxyMethods.size(); i++) { methodNodes.add( createProxyMethodNode( proxyMethods.get(i), i, skeletonId, stubType)); } // Empty methods for (Method emptyMethod : methodsBag.emptyMethods) { MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator( emptyMethod); ASMUtil.addDefaultReturnInsns( methodNodeGenerator, Type.getType(emptyMethod.getReturnType())); methodNodeGenerator.endMethod(); methodNodes.add(methodNodeGenerator.getMethodNode()); } rewriteGetProxyMethodSignaturesMethodNode( classNode, methodsBag.proxyMethodSignatures); return toClass(classNode, classLoader); } protected static Class<?> getSkeletonClass( ClassLoader classLoader, Class<?> clazz) throws Exception { Class<?> skeletonClass = loadClass( classLoader, clazz, SKELETON_POSTFIX); if (skeletonClass != null) { return skeletonClass; } synchronized (classLoader) { skeletonClass = loadClass(classLoader, clazz, SKELETON_POSTFIX); if (skeletonClass != null) { return skeletonClass; } validate(classLoader, clazz, true); skeletonClass = generateSkeletonClass(classLoader, clazz); } return skeletonClass; } protected static Class<?> loadClass( ClassLoader classLoader, Class<?> clazz, String postfix) { String className = clazz.getName(); try { return Class.forName(className.concat(postfix), false, classLoader); } catch (ClassNotFoundException cnfe) { } return null; } protected static void rewriteGetProxyMethodSignaturesMethodNode( ClassNode classNode, String[] proxyMethodSignatures) { MethodNode methodNode = ASMUtil.findMethodNode( classNode.methods, "_getProxyMethodSignatures", _STRING_ARRAY_TYPE); InsnList insnList = methodNode.instructions; insnList.clear(); MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator( methodNode); methodNodeGenerator.push(proxyMethodSignatures.length); methodNodeGenerator.newArray(_STRING_TYPE); for (int i = 0; i < proxyMethodSignatures.length; i++) { methodNodeGenerator.dup(); methodNodeGenerator.push(i); methodNodeGenerator.push(proxyMethodSignatures[i]); methodNodeGenerator.arrayStore(_STRING_TYPE); } methodNodeGenerator.returnValue(); methodNodeGenerator.endMethod(); } protected static void serializerWrite( MethodNodeGenerator methodNodeGenerator, Type type) { String owner = _SERIALIZER_TYPE.getInternalName(); if (type.getSort() <= Type.DOUBLE) { String name = TextFormatter.format( type.getClassName(), TextFormatter.G); methodNodeGenerator.invokeVirtual( owner, "write".concat(name), Type.VOID_TYPE, type); } else if (type.equals(_STRING_TYPE)) { methodNodeGenerator.invokeVirtual( owner, "writeString", Type.VOID_TYPE, _STRING_TYPE); } else { methodNodeGenerator.invokeVirtual( owner, "writeObject", Type.VOID_TYPE, _SERIALIZABLE_TYPE); } } protected static Class<?> toClass( ClassNode classNode, ClassLoader classLoader) { ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); classNode.accept(classWriter); byte[] data = classWriter.toByteArray(); try { if (PropsValues.INTRABAND_PROXY_DUMP_CLASSES_ENABLED) { File classFile = new File( _DUMP_DIR, classNode.name.concat(".class")); FileUtil.write(classFile, data); if (_log.isInfoEnabled()) { _log.info("Dumpped class " + classFile.getAbsolutePath()); } } return (Class<?>)_defineClassMethod.invoke( classLoader, StringUtil.replace( classNode.name, CharPool.SLASH, CharPool.PERIOD), data, 0, data.length); } catch (Exception e) { throw new RuntimeException(e); } } protected static void validate( ClassLoader classLoader, Class<?> clazz, boolean skeletonOrStub) { if (clazz.isAnnotation()) { throw new IllegalArgumentException(clazz + " is an annotation"); } if (clazz.isArray()) { throw new IllegalArgumentException(clazz + " is an array"); } if (clazz.isEnum()) { throw new IllegalArgumentException(clazz + " is an enum"); } if (clazz.isPrimitive()) { throw new IllegalArgumentException(clazz + " is a primitive"); } Class<?> reloadedClass = null; try { reloadedClass = Class.forName(clazz.getName(), false, classLoader); } catch (ClassNotFoundException cnfe) { } if (reloadedClass != clazz) { throw new IllegalArgumentException( clazz + " is not visible from class loader " + classLoader); } Field[] fields = clazz.getDeclaredFields(); checkField( fields, _PROXY_METHOD_SIGNATURES_FIELD_NAME, String[].class, true); if (skeletonOrStub) { checkField( fields, _PROXY_METHODS_MAPPING_FIELD_NAME, String.class, true); checkField(fields, "_log", Log.class, true); checkField(fields, "_targetLocator", TargetLocator.class, false); } else { checkField( fields, "_exceptionHandler", ExceptionHandler.class, false); checkField(fields, "_id", String.class, false); checkField(fields, "_intraband", Intraband.class, false); checkField(fields, "_proxyType", byte.class, true); checkField( fields, "_registrationReference", RegistrationReference.class, false); } } protected static class MethodComparator implements Comparator<Method> { @Override public int compare(Method method1, Method method2) { String methodId1 = _getMethodId(method1); String methodId2 = _getMethodId(method2); return methodId1.compareTo(methodId2); } private static String _getMethodId(Method method) { Proxy proxy = method.getAnnotation(Proxy.class); String methodName = proxy.name(); if (methodName.isEmpty()) { methodName = method.getName(); } return methodName.concat(StringPool.DASH).concat( Type.getMethodDescriptor(method)); } } protected static class MethodsBag { public MethodsBag( List<Method> idMethods, List<Method> proxyMethods, List<Method> emptyMethods) { this.idMethods = idMethods; this.proxyMethods = proxyMethods; this.emptyMethods = emptyMethods; Collections.sort(proxyMethods, _methodComparator); proxyMethodSignatures = new String[proxyMethods.size()]; for (int i = 0; i < proxyMethods.size(); i++) { Method proxyMethod = proxyMethods.get(i); String name = proxyMethod.getName(); proxyMethodSignatures[i] = name.concat(StringPool.DASH).concat( Type.getMethodDescriptor(proxyMethod)); } } protected List<Method> emptyMethods; protected List<Method> idMethods; protected List<Method> proxyMethods; protected String[] proxyMethodSignatures; } protected static class TemplateStub { public static final String[] PROXY_METHOD_SIGNATURES = _getProxyMethodSignatures(); public TemplateStub( String id, RegistrationReference registrationReference, ExceptionHandler exceptionHandler) { if (id == null) { throw new NullPointerException("Id is null"); } if (registrationReference == null) { throw new NullPointerException( "Registration reference is null"); } _id = id; _registrationReference = registrationReference; _exceptionHandler = exceptionHandler; _intraband = registrationReference.getIntraband(); } private static String[] _getProxyMethodSignatures() { return new String[0]; } @SuppressWarnings("unused") private void _send(Serializer serializer) { _intraband.sendDatagram( _registrationReference, Datagram.createRequestDatagram( _PROXY_TYPE, serializer.toByteBuffer())); } @SuppressWarnings("unused") private <T extends Serializable> T _syncSend(Serializer serializer) { try { Datagram responseDatagram = _intraband.sendSyncDatagram( _registrationReference, Datagram.createRequestDatagram( _PROXY_TYPE, serializer.toByteBuffer())); Deserializer deserializer = new Deserializer( responseDatagram.getDataByteBuffer()); RPCResponse rpcResponse = deserializer.readObject(); Exception e = rpcResponse.getException(); if (e != null) { throw e; } return (T)rpcResponse.getResult(); } catch (Exception e) { if (_exceptionHandler != null) { _exceptionHandler.onException(e); } return null; } } private static final byte _PROXY_TYPE = SystemDataType.PROXY.getValue(); private final ExceptionHandler _exceptionHandler; @SuppressWarnings("unused") private String _id; private final Intraband _intraband; private final RegistrationReference _registrationReference; } protected abstract static class TemplateSkeleton implements IntrabandProxySkeleton { public static final String[] PROXY_METHOD_SIGNATURES = _getProxyMethodSignatures(); public TemplateSkeleton(TargetLocator targetLocator) { if (targetLocator == null) { throw new NullPointerException("Target locator is null"); } _targetLocator = targetLocator; } @Override public void dispatch( RegistrationReference registrationReference, Datagram datagram, Deserializer deserializer) { try { doDispatch(registrationReference, datagram, deserializer); } catch (Exception e) { _log.error("Unable to dispatch", e); _sendResponse( registrationReference, datagram, new RPCResponse(e)); } } protected abstract void doDispatch( RegistrationReference registrationReference, Datagram datagram, Deserializer deserializer) throws Exception; private static String[] _getProxyMethodSignatures() { return new String[0]; } private static String _getProxyMethodsMapping( String[] proxyMethodsSignatures) { StringBundler sb = new StringBundler( proxyMethodsSignatures.length * 4 + 1); sb.append(StringPool.OPEN_CURLY_BRACE); for (int i = 0; i < proxyMethodsSignatures.length; i++) { sb.append(i); sb.append(" -> "); sb.append(proxyMethodsSignatures[i]); sb.append(StringPool.COMMA_AND_SPACE); } if (proxyMethodsSignatures.length > 0) { sb.setIndex(sb.index() - 1); } sb.append(StringPool.CLOSE_CURLY_BRACE); return sb.toString(); } private void _sendResponse( RegistrationReference registrationReference, Datagram datagram, RPCResponse rpcResponse) { Serializer serializer = new Serializer(); serializer.writeObject(rpcResponse); Intraband intraband = registrationReference.getIntraband(); intraband.sendDatagram( registrationReference, Datagram.createResponseDatagram( datagram, serializer.toByteBuffer())); } @SuppressWarnings("unused") private void _unknownMethodIndex(int methodIndex) { throw new IllegalArgumentException( "Unknow method index " + methodIndex + " for proxy methods mappings " + _PROXY_METHODS_MAPPING); } private static final String _PROXY_METHODS_MAPPING = _getProxyMethodsMapping(PROXY_METHOD_SIGNATURES); private static final Log _log = LogFactoryUtil.getLog( TemplateSkeleton.class); @SuppressWarnings("unused") private TargetLocator _targetLocator; } private static final Type _DATAGRAM_TYPE = Type.getType(Datagram.class); private static final Type _DESERIALIZER_TYPE = Type.getType( Deserializer.class); private static final File _DUMP_DIR = new File( SystemProperties.get(SystemProperties.TMP_DIR), PropsValues.INTRABAND_PROXY_DUMP_CLASSES_DIR); private static final Type _EXCEPTION_HANDLER_TYPE = Type.getType( ExceptionHandler.class); private static final Type _OBJECT_TYPE = Type.getType(Object.class); private static final String _PROXY_METHOD_SIGNATURES_FIELD_NAME = "PROXY_METHOD_SIGNATURES"; private static final String _PROXY_METHODS_MAPPING_FIELD_NAME = "_PROXY_METHODS_MAPPING"; private static final Type _REGISTRATION_REFERENCE_TYPE = Type.getType( RegistrationReference.class); private static final Type _RPC_RESPONSE_TYPE = Type.getType( RPCResponse.class); private static final Type _SERIALIZABLE_TYPE = Type.getType( Serializable.class); private static final Type _SERIALIZER_TYPE = Type.getType(Serializer.class); private static final Type _STRING_ARRAY_TYPE = Type.getType(String[].class); private static final Type _STRING_TYPE = Type.getType(String.class); private static final Type _TARGET_LOCATOR_TYPE = Type.getType( TargetLocator.class); private static final Log _log = LogFactoryUtil.getLog( IntrabandProxyUtil.class); private static final Set<String> _annotationDescriptors = new HashSet<>( Arrays.asList( Type.getDescriptor(Id.class), Type.getDescriptor(Proxy.class))); private static final Method _defineClassMethod; private static final Comparator<Method> _methodComparator = new MethodComparator(); static { try { _defineClassMethod = ReflectionUtil.getDeclaredMethod( ClassLoader.class, "defineClass", String.class, byte[].class, int.class, int.class); } catch (Throwable t) { throw new ExceptionInInitializerError(t); } } private static class SkeletonDispatchTableSwitchGenerator implements TableSwitchGenerator { public SkeletonDispatchTableSwitchGenerator( MethodNodeGenerator methodNodeGenerator, List<Method> proxyMethods, String owner, int typedTargetIndex, int indexIndex) { _methodNodeGenerator = methodNodeGenerator; _proxyMethods = proxyMethods; _owner = owner; _typedTargetIndex = typedTargetIndex; _indexIndex = indexIndex; } @Override public void generateCase(int key, Label end) { Method proxyMethod = _proxyMethods.get(key); Class<?> returnClass = proxyMethod.getReturnType(); if (returnClass != void.class) { _methodNodeGenerator.loadThis(); _methodNodeGenerator.loadArg(0); _methodNodeGenerator.loadArg(1); _methodNodeGenerator.newInstance(_RPC_RESPONSE_TYPE); _methodNodeGenerator.dup(); } _methodNodeGenerator.loadLocal(_typedTargetIndex); for (Class<?> parameterClass : proxyMethod.getParameterTypes()) { _methodNodeGenerator.loadArg(2); Type parameterType = Type.getType(parameterClass); deserializerRead(_methodNodeGenerator, parameterType); if (!parameterClass.isPrimitive() && (parameterClass != Object.class) && (parameterClass != String.class)) { _methodNodeGenerator.checkCast(parameterType); } } Class<?> declaringClass = proxyMethod.getDeclaringClass(); if (declaringClass.isInterface()) { _methodNodeGenerator.invokeInterface( Type.getInternalName(declaringClass), proxyMethod); } else { _methodNodeGenerator.invokeVirtual( Type.getInternalName(declaringClass), proxyMethod); } if (returnClass != void.class) { if (returnClass.isPrimitive()) { _methodNodeGenerator.box(Type.getType(returnClass)); } else if (Serializable.class.isAssignableFrom(returnClass)) { _methodNodeGenerator.checkCast(_SERIALIZABLE_TYPE); } _methodNodeGenerator.invokeSpecial( _RPC_RESPONSE_TYPE.getInternalName(), "<init>", Type.VOID_TYPE, _SERIALIZABLE_TYPE); _methodNodeGenerator.invokeSpecial( _owner, "_sendResponse", Type.VOID_TYPE, _REGISTRATION_REFERENCE_TYPE, _DATAGRAM_TYPE, _RPC_RESPONSE_TYPE); } _methodNodeGenerator.goTo(end); } @Override public void generateDefault() { _methodNodeGenerator.loadThis(); _methodNodeGenerator.loadLocal(_indexIndex); _methodNodeGenerator.invokeSpecial( _owner, "_unknownMethodIndex", Type.VOID_TYPE, Type.INT_TYPE); } private final int _indexIndex; private final MethodNodeGenerator _methodNodeGenerator; private final String _owner; private final List<Method> _proxyMethods; private final int _typedTargetIndex; } }