001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.nio.intraband.proxy;
016    
017    import com.liferay.portal.asm.ASMUtil;
018    import com.liferay.portal.asm.MethodNodeGenerator;
019    import com.liferay.portal.kernel.io.Deserializer;
020    import com.liferay.portal.kernel.io.Serializer;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.nio.intraband.Datagram;
024    import com.liferay.portal.kernel.nio.intraband.Intraband;
025    import com.liferay.portal.kernel.nio.intraband.RegistrationReference;
026    import com.liferay.portal.kernel.nio.intraband.SystemDataType;
027    import com.liferay.portal.kernel.nio.intraband.proxy.ExceptionHandler;
028    import com.liferay.portal.kernel.nio.intraband.proxy.IntrabandProxySkeleton;
029    import com.liferay.portal.kernel.nio.intraband.proxy.TargetLocator;
030    import com.liferay.portal.kernel.nio.intraband.proxy.annotation.Id;
031    import com.liferay.portal.kernel.nio.intraband.proxy.annotation.Proxy;
032    import com.liferay.portal.kernel.nio.intraband.rpc.RPCResponse;
033    import com.liferay.portal.kernel.util.CharPool;
034    import com.liferay.portal.kernel.util.FileUtil;
035    import com.liferay.portal.kernel.util.ReflectionUtil;
036    import com.liferay.portal.kernel.util.StringBundler;
037    import com.liferay.portal.kernel.util.StringPool;
038    import com.liferay.portal.kernel.util.StringUtil;
039    import com.liferay.portal.kernel.util.SystemProperties;
040    import com.liferay.portal.kernel.util.TextFormatter;
041    import com.liferay.portal.util.PropsValues;
042    
043    import java.io.File;
044    import java.io.Serializable;
045    
046    import java.lang.reflect.Constructor;
047    import java.lang.reflect.Field;
048    import java.lang.reflect.Method;
049    import java.lang.reflect.Modifier;
050    
051    import java.util.ArrayList;
052    import java.util.Arrays;
053    import java.util.Collections;
054    import java.util.Comparator;
055    import java.util.HashSet;
056    import java.util.List;
057    import java.util.Set;
058    
059    import org.objectweb.asm.ClassWriter;
060    import org.objectweb.asm.Label;
061    import org.objectweb.asm.Opcodes;
062    import org.objectweb.asm.Type;
063    import org.objectweb.asm.commons.TableSwitchGenerator;
064    import org.objectweb.asm.tree.ClassNode;
065    import org.objectweb.asm.tree.FieldNode;
066    import org.objectweb.asm.tree.InsnList;
067    import org.objectweb.asm.tree.MethodNode;
068    
069    /**
070     * @author Shuyang Zhou
071     */
072    public class IntrabandProxyUtil {
073    
074            public static final String SKELETON_POSTFIX = "__IntrabandProxy__Skeleton";
075    
076            public static final String STUB_POSTFIX = "__IntrabandProxy__Stub";
077    
078            public static String[] getProxyMethodSignatures(Class<?> clazz) {
079                    try {
080                            Field field = clazz.getField(_PROXY_METHOD_SIGNATURES_FIELD_NAME);
081    
082                            return (String[])field.get(null);
083                    }
084                    catch (Exception e) {
085                            return null;
086                    }
087            }
088    
089            public static Class<?> getStubClass(Class<?> clazz, String skeletonId) {
090                    return getStubClass(clazz.getClassLoader(), clazz, skeletonId);
091            }
092    
093            public static Class<?> getStubClass(
094                    ClassLoader classLoader, Class<?> clazz, String skeletonId) {
095    
096                    Class<?> stubClass = loadClass(classLoader, clazz, STUB_POSTFIX);
097    
098                    if (stubClass != null) {
099                            return stubClass;
100                    }
101    
102                    synchronized (classLoader) {
103                            stubClass = loadClass(classLoader, clazz, STUB_POSTFIX);
104    
105                            if (stubClass != null) {
106                                    return stubClass;
107                            }
108    
109                            validate(classLoader, clazz, false);
110    
111                            stubClass = generateStubClass(classLoader, clazz, skeletonId);
112                    }
113    
114                    return stubClass;
115            }
116    
117            public static <T> T newStubInstance(
118                    Class<? extends T> stubClass, String id,
119                    RegistrationReference registrationReference,
120                    ExceptionHandler exceptionHandler) {
121    
122                    try {
123                            Constructor<? extends T> constructor = stubClass.getConstructor(
124                                    String.class, RegistrationReference.class,
125                                    ExceptionHandler.class);
126    
127                            return constructor.newInstance(
128                                    id, registrationReference, exceptionHandler);
129                    }
130                    catch (Exception e) {
131                            throw new RuntimeException(e);
132                    }
133            }
134    
135            protected static void checkField(
136                    Field[] fields, String name, Class<?> clazz, boolean isStatic) {
137    
138                    for (Field field : fields) {
139                            if (name.equals(field.getName())) {
140                                    if ((field.getType() != clazz) ||
141                                            (Modifier.isStatic(field.getModifiers()) != isStatic)) {
142    
143                                            throw new IllegalArgumentException(
144                                                    "Field " + field + " is expected to be of type " +
145                                                            clazz + " and " + (!isStatic ? "not " : "") +
146                                                                    "static");
147                                    }
148    
149                                    break;
150                            }
151                    }
152            }
153    
154            protected static MethodNode createProxyMethodNode(
155                    Method method, int index, String skeletonId, Type stubType) {
156    
157                    MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator(
158                            method);
159    
160                    // Serializer serializer = new Serializer();
161    
162                    methodNodeGenerator.newInstance(_SERIALIZER_TYPE);
163    
164                    methodNodeGenerator.dup();
165    
166                    methodNodeGenerator.invokeSpecial(
167                            _SERIALIZER_TYPE.getInternalName(), "<init>", Type.VOID_TYPE);
168    
169                    int serializerIndex = methodNodeGenerator.newLocal(_SERIALIZER_TYPE);
170    
171                    methodNodeGenerator.storeLocal(serializerIndex);
172    
173                    // serializer.writeString(skeletonId);
174    
175                    methodNodeGenerator.loadLocal(serializerIndex);
176    
177                    methodNodeGenerator.push(skeletonId);
178    
179                    serializerWrite(methodNodeGenerator, _STRING_TYPE);
180    
181                    // serializer.writeString(_id);
182    
183                    methodNodeGenerator.loadLocal(serializerIndex);
184    
185                    methodNodeGenerator.loadThis();
186    
187                    methodNodeGenerator.getField(stubType, "_id", _STRING_TYPE);
188    
189                    serializerWrite(methodNodeGenerator, _STRING_TYPE);
190    
191                    // serializer.writeInt(index);
192    
193                    methodNodeGenerator.loadLocal(serializerIndex);
194    
195                    methodNodeGenerator.push(index);
196    
197                    serializerWrite(methodNodeGenerator, Type.INT_TYPE);
198    
199                    // serializer.writeXXX(parameters...)
200    
201                    Class<?>[] parameterTypes = method.getParameterTypes();
202    
203                    for (int i = 0; i < parameterTypes.length; i++) {
204                            methodNodeGenerator.loadLocal(serializerIndex);
205    
206                            methodNodeGenerator.loadArg(i);
207    
208                            serializerWrite(
209                                    methodNodeGenerator, Type.getType(parameterTypes[i]));
210                    }
211    
212                    // this._send(Serializer) / return this._syncSend(Serializer)
213    
214                    methodNodeGenerator.loadThis();
215    
216                    methodNodeGenerator.loadLocal(serializerIndex);
217    
218                    Class<?> returnClass = method.getReturnType();
219    
220                    if (returnClass == void.class) {
221    
222                            // this._send(Serializer)
223    
224                            methodNodeGenerator.invokeSpecial(
225                                    stubType.getInternalName(), "_send", Type.VOID_TYPE,
226                                    _SERIALIZER_TYPE);
227    
228                            methodNodeGenerator.returnValue();
229                    }
230                    else {
231    
232                            // return this._syncSend(Serializer)
233    
234                            methodNodeGenerator.invokeSpecial(
235                                    stubType.getInternalName(), "_syncSend", _SERIALIZABLE_TYPE,
236                                    _SERIALIZER_TYPE);
237    
238                            Type returnType = Type.getType(returnClass);
239    
240                            if (returnClass.isPrimitive()) {
241    
242                                    // Null check and unbox
243    
244                                    int returnValueIndex = methodNodeGenerator.newLocal(
245                                            _OBJECT_TYPE);
246    
247                                    methodNodeGenerator.storeLocal(returnValueIndex);
248    
249                                    methodNodeGenerator.loadLocal(returnValueIndex);
250    
251                                    Label nullCheckLabel = new Label();
252    
253                                    methodNodeGenerator.ifNull(nullCheckLabel);
254    
255                                    methodNodeGenerator.loadLocal(returnValueIndex);
256    
257                                    methodNodeGenerator.unbox(returnType);
258    
259                                    methodNodeGenerator.returnValue();
260    
261                                    methodNodeGenerator.visitLabel(nullCheckLabel);
262    
263                                    ASMUtil.addDefaultReturnInsns(methodNodeGenerator, returnType);
264                            }
265                            else {
266                                    if (returnClass != Object.class) {
267                                            methodNodeGenerator.checkCast(returnType);
268                                    }
269    
270                                    methodNodeGenerator.returnValue();
271                            }
272                    }
273    
274                    methodNodeGenerator.endMethod();
275    
276                    return methodNodeGenerator.getMethodNode();
277            }
278    
279            protected static void deserializerRead(
280                    MethodNodeGenerator methodNodeGenerator, Type type) {
281    
282                    String owner = _DESERIALIZER_TYPE.getInternalName();
283    
284                    if (type.getSort() <= Type.DOUBLE) {
285                            String name = TextFormatter.format(
286                                    type.getClassName(), TextFormatter.G);
287    
288                            methodNodeGenerator.invokeVirtual(owner, "read".concat(name), type);
289                    }
290                    else if (type.equals(_STRING_TYPE)) {
291                            methodNodeGenerator.invokeVirtual(
292                                    owner, "readString", _STRING_TYPE);
293                    }
294                    else {
295                            methodNodeGenerator.invokeVirtual(
296                                    owner, "readObject", _SERIALIZABLE_TYPE);
297                    }
298            }
299    
300            protected static MethodsBag extractMethods(Class<?> clazz) {
301                    List<Method> idMethods = new ArrayList<>();
302                    List<Method> proxyMethods = new ArrayList<>();
303                    List<Method> emptyMethods = new ArrayList<>();
304    
305                    for (Method method : ReflectionUtil.getVisibleMethods(clazz)) {
306                            Id id = method.getAnnotation(Id.class);
307    
308                            if (id != null) {
309                                    if (Modifier.isStatic(method.getModifiers())) {
310                                            throw new IllegalArgumentException(
311                                                    "The @Id annotated method " + method +
312                                                            " must not be static");
313                                    }
314    
315                                    Class<?>[] parameterTypes = method.getParameterTypes();
316    
317                                    if (parameterTypes.length > 0) {
318                                            throw new IllegalArgumentException(
319                                                    "The @Id annotated method " + method +
320                                                            " must not have parameters");
321                                    }
322    
323                                    if (method.getReturnType() != String.class) {
324                                            throw new IllegalArgumentException(
325                                                    "The @Id annotated method " + method +
326                                                            " must not return String");
327                                    }
328    
329                                    idMethods.add(method);
330    
331                                    continue;
332                            }
333    
334                            Proxy proxy = method.getAnnotation(Proxy.class);
335    
336                            if (proxy != null) {
337                                    if (Modifier.isStatic(method.getModifiers())) {
338                                            throw new IllegalArgumentException(
339                                                    "Static proxy method violation for " + method);
340                                    }
341    
342                                    proxyMethods.add(method);
343    
344                                    continue;
345                            }
346    
347                            if (Modifier.isAbstract(method.getModifiers())) {
348                                    emptyMethods.add(method);
349                            }
350                    }
351    
352                    return new MethodsBag(idMethods, proxyMethods, emptyMethods);
353            }
354    
355            protected static Class<? extends IntrabandProxySkeleton>
356                    generateSkeletonClass(ClassLoader classLoader, Class<?> clazz) {
357    
358                    Type targetType = Type.getType(clazz);
359    
360                    String internalName = targetType.getInternalName();
361    
362                    ClassNode classNode = ASMUtil.loadAndRename(
363                            TemplateSkeleton.class, internalName.concat(SKELETON_POSTFIX));
364    
365                    classNode.access &= ~Opcodes.ACC_ABSTRACT;
366                    classNode.access |= Opcodes.ACC_PUBLIC;
367    
368                    FieldNode proxyMethodsMappingFieldNode = ASMUtil.findFieldNode(
369                            classNode.fields, "_PROXY_METHODS_MAPPING");
370    
371                    proxyMethodsMappingFieldNode.access |= Opcodes.ACC_FINAL;
372    
373                    FieldNode targetLocatorFieldNode = ASMUtil.findFieldNode(
374                            classNode.fields, "_targetLocator");
375    
376                    targetLocatorFieldNode.access |= Opcodes.ACC_FINAL;
377    
378                    MethodNode doDispatchMethodNode = ASMUtil.findMethodNode(
379                            classNode.methods, "doDispatch", Type.VOID_TYPE,
380                            _REGISTRATION_REFERENCE_TYPE, _DATAGRAM_TYPE, _DESERIALIZER_TYPE);
381    
382                    doDispatchMethodNode.access &= ~Opcodes.ACC_ABSTRACT;
383    
384                    MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator(
385                            doDispatchMethodNode);
386    
387                    // T target = (T)_targetLocator.getTarget()
388    
389                    methodNodeGenerator.loadThis();
390    
391                    methodNodeGenerator.getField(
392                            Type.getObjectType(classNode.name), "_targetLocator",
393                            _TARGET_LOCATOR_TYPE);
394    
395                    methodNodeGenerator.loadArg(2);
396    
397                    deserializerRead(methodNodeGenerator, _STRING_TYPE);
398    
399                    methodNodeGenerator.invokeInterface(
400                            _TARGET_LOCATOR_TYPE.getInternalName(), "getTarget", _OBJECT_TYPE,
401                            _STRING_TYPE);
402    
403                    methodNodeGenerator.checkCast(targetType);
404    
405                    int typedTargetIndex = methodNodeGenerator.newLocal(targetType);
406    
407                    methodNodeGenerator.storeLocal(typedTargetIndex);
408    
409                    // int index = deserializer.readInt();
410    
411                    methodNodeGenerator.loadArg(2);
412    
413                    deserializerRead(methodNodeGenerator, Type.INT_TYPE);
414    
415                    methodNodeGenerator.dup();
416    
417                    int indexIndex = methodNodeGenerator.newLocal(Type.INT_TYPE);
418    
419                    methodNodeGenerator.storeLocal(indexIndex);
420    
421                    // switch (index)
422    
423                    MethodsBag methodsBag = extractMethods(clazz);
424    
425                    List<Method> proxyMethods = methodsBag.proxyMethods;
426    
427                    int[] keys = new int[proxyMethods.size()];
428    
429                    for (int i = 0; i < keys.length; i++) {
430                            keys[i] = i;
431                    }
432    
433                    methodNodeGenerator.tableSwitch(
434                            keys,
435                            new SkeletonDispatchTableSwitchGenerator(
436                                    methodNodeGenerator, proxyMethods, classNode.name,
437                                    typedTargetIndex, indexIndex),
438                            true);
439    
440                    methodNodeGenerator.returnValue();
441    
442                    methodNodeGenerator.endMethod();
443    
444                    rewriteGetProxyMethodSignaturesMethodNode(
445                            classNode, methodsBag.proxyMethodSignatures);
446    
447                    return (Class<? extends IntrabandProxySkeleton>)toClass(
448                            classNode, classLoader);
449            }
450    
451            protected static Class<?> generateStubClass(
452                    ClassLoader classLoader, Class<?> clazz, String skeletonId) {
453    
454                    String internalName = Type.getInternalName(clazz);
455    
456                    ClassNode classNode = ASMUtil.loadAndRename(
457                            clazz, internalName.concat(STUB_POSTFIX));
458    
459                    // Class modifiers and hierarchy
460    
461                    classNode.access &= ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE);
462                    classNode.access |= Opcodes.ACC_PUBLIC;
463    
464                    if (clazz.isInterface()) {
465                            List<String> interfaces = classNode.interfaces;
466    
467                            interfaces.clear();
468    
469                            interfaces.add(internalName);
470                    }
471    
472                    // Clean up MethodNodes that are going to be generated
473    
474                    List<MethodNode> methodNodes = classNode.methods;
475    
476                    MethodNode defaultInitMethodNode = ASMUtil.removeMethodNode(
477                            methodNodes, "<init>", Type.VOID_TYPE);
478    
479                    ASMUtil.removeMethodNodes(methodNodes, "<init>");
480                    ASMUtil.removeMethodNodes(methodNodes, Opcodes.ACC_ABSTRACT);
481                    ASMUtil.removeMethodNodes(methodNodes, _annotationDescriptors);
482    
483                    // Apply template class
484    
485                    ClassNode templateClassNode = ASMUtil.loadAndRename(
486                            TemplateStub.class, classNode.name);
487    
488                    List<FieldNode> templateFieldNodes = templateClassNode.fields;
489    
490                    FieldNode idFieldNode = ASMUtil.findFieldNode(
491                            templateFieldNodes, "_id");
492    
493                    idFieldNode.access |= Opcodes.ACC_FINAL;
494    
495                    FieldNode intrabandFieldNode = ASMUtil.findFieldNode(
496                            templateFieldNodes, "_intraband");
497    
498                    intrabandFieldNode.access |= Opcodes.ACC_FINAL;
499    
500                    FieldNode registrationReferenceFieldNode = ASMUtil.findFieldNode(
501                            templateFieldNodes, "_registrationReference");
502    
503                    registrationReferenceFieldNode.access |= Opcodes.ACC_FINAL;
504    
505                    ASMUtil.addFieldNodes(classNode.fields, templateFieldNodes);
506    
507                    List<MethodNode> templateMethodNodes = templateClassNode.methods;
508    
509                    MethodNode templateInitMethodNode = ASMUtil.findMethodNode(
510                            templateMethodNodes, "<init>", Type.VOID_TYPE, _STRING_TYPE,
511                            _REGISTRATION_REFERENCE_TYPE, _EXCEPTION_HANDLER_TYPE);
512    
513                    if (defaultInitMethodNode != null) {
514                            ASMUtil.mergeMethods(
515                                    templateInitMethodNode, defaultInitMethodNode,
516                                    templateInitMethodNode);
517                    }
518    
519                    MethodNode defaultClinitMethodNode = ASMUtil.removeMethodNode(
520                            methodNodes, "<clinit>", Type.VOID_TYPE);
521    
522                    if (defaultClinitMethodNode != null) {
523                            MethodNode templateClinitMethodNode = ASMUtil.findMethodNode(
524                                    templateMethodNodes, "<clinit>", Type.VOID_TYPE);
525    
526                            ASMUtil.mergeMethods(
527                                    templateClinitMethodNode, defaultClinitMethodNode,
528                                    templateClinitMethodNode);
529                    }
530    
531                    methodNodes.addAll(templateMethodNodes);
532    
533                    Type stubType = Type.getType(classNode.name);
534    
535                    // Id methods
536    
537                    MethodsBag methodsBag = extractMethods(clazz);
538    
539                    for (Method idMethod : methodsBag.idMethods) {
540                            MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator(
541                                    idMethod);
542    
543                            methodNodeGenerator.loadThis();
544    
545                            methodNodeGenerator.getField(stubType, "_id", _STRING_TYPE);
546    
547                            methodNodeGenerator.returnValue();
548    
549                            methodNodeGenerator.endMethod();
550    
551                            methodNodes.add(methodNodeGenerator.getMethodNode());
552                    }
553    
554                    // Proxy methods
555    
556                    List<Method> proxyMethods = methodsBag.proxyMethods;
557    
558                    for (int i = 0; i < proxyMethods.size(); i++) {
559                            methodNodes.add(
560                                    createProxyMethodNode(
561                                            proxyMethods.get(i), i, skeletonId, stubType));
562                    }
563    
564                    // Empty methods
565    
566                    for (Method emptyMethod : methodsBag.emptyMethods) {
567                            MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator(
568                                    emptyMethod);
569    
570                            ASMUtil.addDefaultReturnInsns(
571                                    methodNodeGenerator, Type.getType(emptyMethod.getReturnType()));
572    
573                            methodNodeGenerator.endMethod();
574    
575                            methodNodes.add(methodNodeGenerator.getMethodNode());
576                    }
577    
578                    rewriteGetProxyMethodSignaturesMethodNode(
579                            classNode, methodsBag.proxyMethodSignatures);
580    
581                    return toClass(classNode, classLoader);
582            }
583    
584            protected static Class<?> getSkeletonClass(
585                            ClassLoader classLoader, Class<?> clazz)
586                    throws Exception {
587    
588                    Class<?> skeletonClass = loadClass(
589                            classLoader, clazz, SKELETON_POSTFIX);
590    
591                    if (skeletonClass != null) {
592                            return skeletonClass;
593                    }
594    
595                    synchronized (classLoader) {
596                            skeletonClass = loadClass(classLoader, clazz, SKELETON_POSTFIX);
597    
598                            if (skeletonClass != null) {
599                                    return skeletonClass;
600                            }
601    
602                            validate(classLoader, clazz, true);
603    
604                            skeletonClass = generateSkeletonClass(classLoader, clazz);
605                    }
606    
607                    return skeletonClass;
608            }
609    
610            protected static Class<?> loadClass(
611                    ClassLoader classLoader, Class<?> clazz, String postfix) {
612    
613                    String className = clazz.getName();
614    
615                    try {
616                            return Class.forName(className.concat(postfix), false, classLoader);
617                    }
618                    catch (ClassNotFoundException cnfe) {
619                    }
620    
621                    return null;
622            }
623    
624            protected static void rewriteGetProxyMethodSignaturesMethodNode(
625                    ClassNode classNode, String[] proxyMethodSignatures) {
626    
627                    MethodNode methodNode = ASMUtil.findMethodNode(
628                            classNode.methods, "_getProxyMethodSignatures", _STRING_ARRAY_TYPE);
629    
630                    InsnList insnList = methodNode.instructions;
631    
632                    insnList.clear();
633    
634                    MethodNodeGenerator methodNodeGenerator = new MethodNodeGenerator(
635                            methodNode);
636    
637                    methodNodeGenerator.push(proxyMethodSignatures.length);
638    
639                    methodNodeGenerator.newArray(_STRING_TYPE);
640    
641                    for (int i = 0; i < proxyMethodSignatures.length; i++) {
642                            methodNodeGenerator.dup();
643    
644                            methodNodeGenerator.push(i);
645                            methodNodeGenerator.push(proxyMethodSignatures[i]);
646    
647                            methodNodeGenerator.arrayStore(_STRING_TYPE);
648                    }
649    
650                    methodNodeGenerator.returnValue();
651    
652                    methodNodeGenerator.endMethod();
653            }
654    
655            protected static void serializerWrite(
656                    MethodNodeGenerator methodNodeGenerator, Type type) {
657    
658                    String owner = _SERIALIZER_TYPE.getInternalName();
659    
660                    if (type.getSort() <= Type.DOUBLE) {
661                            String name = TextFormatter.format(
662                                    type.getClassName(), TextFormatter.G);
663    
664                            methodNodeGenerator.invokeVirtual(
665                                    owner, "write".concat(name), Type.VOID_TYPE, type);
666                    }
667                    else if (type.equals(_STRING_TYPE)) {
668                            methodNodeGenerator.invokeVirtual(
669                                    owner, "writeString", Type.VOID_TYPE, _STRING_TYPE);
670                    }
671                    else {
672                            methodNodeGenerator.invokeVirtual(
673                                    owner, "writeObject", Type.VOID_TYPE, _SERIALIZABLE_TYPE);
674                    }
675            }
676    
677            protected static Class<?> toClass(
678                    ClassNode classNode, ClassLoader classLoader) {
679    
680                    ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
681    
682                    classNode.accept(classWriter);
683    
684                    byte[] data = classWriter.toByteArray();
685    
686                    try {
687                            if (PropsValues.INTRABAND_PROXY_DUMP_CLASSES_ENABLED) {
688                                    File classFile = new File(
689                                            _DUMP_DIR, classNode.name.concat(".class"));
690    
691                                    FileUtil.write(classFile, data);
692    
693                                    if (_log.isInfoEnabled()) {
694                                            _log.info("Dumpped class " + classFile.getAbsolutePath());
695                                    }
696                            }
697    
698                            return (Class<?>)_defineClassMethod.invoke(
699                                    classLoader,
700                                    StringUtil.replace(
701                                            classNode.name, CharPool.SLASH, CharPool.PERIOD),
702                                    data, 0, data.length);
703                    }
704                    catch (Exception e) {
705                            throw new RuntimeException(e);
706                    }
707            }
708    
709            protected static void validate(
710                    ClassLoader classLoader, Class<?> clazz, boolean skeletonOrStub) {
711    
712                    if (clazz.isAnnotation()) {
713                            throw new IllegalArgumentException(clazz + " is an annotation");
714                    }
715    
716                    if (clazz.isArray()) {
717                            throw new IllegalArgumentException(clazz + " is an array");
718                    }
719    
720                    if (clazz.isEnum()) {
721                            throw new IllegalArgumentException(clazz + " is an enum");
722                    }
723    
724                    if (clazz.isPrimitive()) {
725                            throw new IllegalArgumentException(clazz + " is a primitive");
726                    }
727    
728                    Class<?> reloadedClass = null;
729    
730                    try {
731                            reloadedClass = Class.forName(clazz.getName(), false, classLoader);
732                    }
733                    catch (ClassNotFoundException cnfe) {
734                    }
735    
736                    if (reloadedClass != clazz) {
737                            throw new IllegalArgumentException(
738                                    clazz + " is not visible from class loader " + classLoader);
739                    }
740    
741                    Field[] fields = clazz.getDeclaredFields();
742    
743                    checkField(
744                            fields, _PROXY_METHOD_SIGNATURES_FIELD_NAME, String[].class, true);
745    
746                    if (skeletonOrStub) {
747                            checkField(
748                                    fields, _PROXY_METHODS_MAPPING_FIELD_NAME, String.class, true);
749                            checkField(fields, "_log", Log.class, true);
750                            checkField(fields, "_targetLocator", TargetLocator.class, false);
751                    }
752                    else {
753                            checkField(
754                                    fields, "_exceptionHandler", ExceptionHandler.class, false);
755                            checkField(fields, "_id", String.class, false);
756                            checkField(fields, "_intraband", Intraband.class, false);
757                            checkField(fields, "_proxyType", byte.class, true);
758                            checkField(
759                                    fields, "_registrationReference", RegistrationReference.class,
760                                    false);
761                    }
762            }
763    
764            protected static class MethodComparator implements Comparator<Method> {
765    
766                    @Override
767                    public int compare(Method method1, Method method2) {
768                            String methodId1 = _getMethodId(method1);
769                            String methodId2 = _getMethodId(method2);
770    
771                            return methodId1.compareTo(methodId2);
772                    }
773    
774                    private static String _getMethodId(Method method) {
775                            Proxy proxy = method.getAnnotation(Proxy.class);
776    
777                            String methodName = proxy.name();
778    
779                            if (methodName.isEmpty()) {
780                                    methodName = method.getName();
781                            }
782    
783                            return methodName.concat(StringPool.DASH).concat(
784                                    Type.getMethodDescriptor(method));
785                    }
786    
787            }
788    
789            protected static class MethodsBag {
790    
791                    public MethodsBag(
792                            List<Method> idMethods, List<Method> proxyMethods,
793                            List<Method> emptyMethods) {
794    
795                            this.idMethods = idMethods;
796                            this.proxyMethods = proxyMethods;
797                            this.emptyMethods = emptyMethods;
798    
799                            Collections.sort(proxyMethods, _methodComparator);
800    
801                            proxyMethodSignatures = new String[proxyMethods.size()];
802    
803                            for (int i = 0; i < proxyMethods.size(); i++) {
804                                    Method proxyMethod = proxyMethods.get(i);
805    
806                                    String name = proxyMethod.getName();
807    
808                                    proxyMethodSignatures[i] = name.concat(StringPool.DASH).concat(
809                                            Type.getMethodDescriptor(proxyMethod));
810                            }
811                    }
812    
813                    protected List<Method> emptyMethods;
814                    protected List<Method> idMethods;
815                    protected List<Method> proxyMethods;
816                    protected String[] proxyMethodSignatures;
817    
818            }
819    
820            protected static abstract class TemplateSkeleton
821                    implements IntrabandProxySkeleton {
822    
823                    public static final String[] PROXY_METHOD_SIGNATURES =
824                            _getProxyMethodSignatures();
825    
826                    public TemplateSkeleton(TargetLocator targetLocator) {
827                            if (targetLocator == null) {
828                                    throw new NullPointerException("Target locator is null");
829                            }
830    
831                            _targetLocator = targetLocator;
832                    }
833    
834                    @Override
835                    public void dispatch(
836                            RegistrationReference registrationReference, Datagram datagram,
837                            Deserializer deserializer) {
838    
839                            try {
840                                    doDispatch(registrationReference, datagram, deserializer);
841                            }
842                            catch (Exception e) {
843                                    _log.error("Unable to dispatch", e);
844    
845                                    _sendResponse(
846                                            registrationReference, datagram, new RPCResponse(e));
847                            }
848                    }
849    
850                    protected abstract void doDispatch(
851                                    RegistrationReference registrationReference, Datagram datagram,
852                                    Deserializer deserializer)
853                            throws Exception;
854    
855                    private static String[] _getProxyMethodSignatures() {
856                            return new String[0];
857                    }
858    
859                    private static String _getProxyMethodsMapping(
860                            String[] proxyMethodsSignatures) {
861    
862                            StringBundler sb = new StringBundler(
863                                    proxyMethodsSignatures.length * 4 + 1);
864    
865                            sb.append(StringPool.OPEN_CURLY_BRACE);
866    
867                            for (int i = 0; i < proxyMethodsSignatures.length; i++) {
868                                    sb.append(i);
869                                    sb.append(" -> ");
870                                    sb.append(proxyMethodsSignatures[i]);
871                                    sb.append(StringPool.COMMA_AND_SPACE);
872                            }
873    
874                            if (proxyMethodsSignatures.length > 0) {
875                                    sb.setIndex(sb.index() - 1);
876                            }
877    
878                            sb.append(StringPool.CLOSE_CURLY_BRACE);
879    
880                            return sb.toString();
881                    }
882    
883                    private void _sendResponse(
884                            RegistrationReference registrationReference, Datagram datagram,
885                            RPCResponse rpcResponse) {
886    
887                            Serializer serializer = new Serializer();
888    
889                            serializer.writeObject(rpcResponse);
890    
891                            Intraband intraband = registrationReference.getIntraband();
892    
893                            intraband.sendDatagram(
894                                    registrationReference,
895                                    Datagram.createResponseDatagram(
896                                            datagram, serializer.toByteBuffer()));
897                    }
898    
899                    @SuppressWarnings("unused")
900                    private void _unknownMethodIndex(int methodIndex) {
901                            throw new IllegalArgumentException(
902                                    "Unknow method index " + methodIndex +
903                                            " for proxy methods mappings " + _PROXY_METHODS_MAPPING);
904                    }
905    
906                    private static final String _PROXY_METHODS_MAPPING =
907                            _getProxyMethodsMapping(PROXY_METHOD_SIGNATURES);
908    
909                    private static final Log _log = LogFactoryUtil.getLog(
910                            TemplateSkeleton.class);
911    
912                    @SuppressWarnings("unused")
913                    private TargetLocator _targetLocator;
914    
915            }
916    
917            protected static class TemplateStub {
918    
919                    public static final String[] PROXY_METHOD_SIGNATURES =
920                            _getProxyMethodSignatures();
921    
922                    public TemplateStub(
923                            String id, RegistrationReference registrationReference,
924                            ExceptionHandler exceptionHandler) {
925    
926                            if (id == null) {
927                                    throw new NullPointerException("Id is null");
928                            }
929    
930                            if (registrationReference == null) {
931                                    throw new NullPointerException(
932                                            "Registration reference is null");
933                            }
934    
935                            _id = id;
936                            _registrationReference = registrationReference;
937                            _exceptionHandler = exceptionHandler;
938    
939                            _intraband = registrationReference.getIntraband();
940                    }
941    
942                    private static String[] _getProxyMethodSignatures() {
943                            return new String[0];
944                    }
945    
946                    @SuppressWarnings("unused")
947                    private void _send(Serializer serializer) {
948                            _intraband.sendDatagram(
949                                    _registrationReference,
950                                    Datagram.createRequestDatagram(
951                                            _PROXY_TYPE, serializer.toByteBuffer()));
952                    }
953    
954                    @SuppressWarnings("unused")
955                    private <T extends Serializable> T _syncSend(Serializer serializer) {
956                            try {
957                                    Datagram responseDatagram = _intraband.sendSyncDatagram(
958                                            _registrationReference,
959                                            Datagram.createRequestDatagram(
960                                                    _PROXY_TYPE, serializer.toByteBuffer()));
961    
962                                    Deserializer deserializer = new Deserializer(
963                                            responseDatagram.getDataByteBuffer());
964    
965                                    RPCResponse rpcResponse = deserializer.readObject();
966    
967                                    Exception e = rpcResponse.getException();
968    
969                                    if (e != null) {
970                                            throw e;
971                                    }
972    
973                                    return (T)rpcResponse.getResult();
974                            }
975                            catch (Exception e) {
976                                    if (_exceptionHandler != null) {
977                                            _exceptionHandler.onException(e);
978                                    }
979    
980                                    return null;
981                            }
982                    }
983    
984                    private static final byte _PROXY_TYPE = SystemDataType.PROXY.getValue();
985    
986                    private final ExceptionHandler _exceptionHandler;
987    
988                    @SuppressWarnings("unused")
989                    private String _id;
990    
991                    private final Intraband _intraband;
992                    private final RegistrationReference _registrationReference;
993    
994            }
995    
996            private static final Type _DATAGRAM_TYPE = Type.getType(Datagram.class);
997    
998            private static final Type _DESERIALIZER_TYPE = Type.getType(
999                    Deserializer.class);
1000    
1001            private static final File _DUMP_DIR = new File(
1002                    SystemProperties.get(SystemProperties.TMP_DIR),
1003                    PropsValues.INTRABAND_PROXY_DUMP_CLASSES_DIR);
1004    
1005            private static final Type _EXCEPTION_HANDLER_TYPE = Type.getType(
1006                    ExceptionHandler.class);
1007    
1008            private static final Type _OBJECT_TYPE = Type.getType(Object.class);
1009    
1010            private static final String _PROXY_METHOD_SIGNATURES_FIELD_NAME =
1011                    "PROXY_METHOD_SIGNATURES";
1012    
1013            private static final String _PROXY_METHODS_MAPPING_FIELD_NAME =
1014                    "_PROXY_METHODS_MAPPING";
1015    
1016            private static final Type _REGISTRATION_REFERENCE_TYPE = Type.getType(
1017                    RegistrationReference.class);
1018    
1019            private static final Type _RPC_RESPONSE_TYPE = Type.getType(
1020                    RPCResponse.class);
1021    
1022            private static final Type _SERIALIZABLE_TYPE = Type.getType(
1023                    Serializable.class);
1024    
1025            private static final Type _SERIALIZER_TYPE = Type.getType(Serializer.class);
1026    
1027            private static final Type _STRING_ARRAY_TYPE = Type.getType(String[].class);
1028    
1029            private static final Type _STRING_TYPE = Type.getType(String.class);
1030    
1031            private static final Type _TARGET_LOCATOR_TYPE = Type.getType(
1032                    TargetLocator.class);
1033    
1034            private static final Log _log = LogFactoryUtil.getLog(
1035                    IntrabandProxyUtil.class);
1036    
1037            private static final Set<String> _annotationDescriptors =
1038                    new HashSet<String>(
1039                            Arrays.asList(
1040                                    Type.getDescriptor(Id.class), Type.getDescriptor(Proxy.class)));
1041            private static final Method _defineClassMethod;
1042            private static final Comparator<Method> _methodComparator =
1043                    new MethodComparator();
1044    
1045            static {
1046                    try {
1047                            _defineClassMethod = ReflectionUtil.getDeclaredMethod(
1048                                    ClassLoader.class, "defineClass", String.class, byte[].class,
1049                                    int.class, int.class);
1050                    }
1051                    catch (Throwable t) {
1052                            throw new ExceptionInInitializerError(t);
1053                    }
1054            }
1055    
1056            private static class SkeletonDispatchTableSwitchGenerator
1057                    implements TableSwitchGenerator {
1058    
1059                    public SkeletonDispatchTableSwitchGenerator(
1060                            MethodNodeGenerator methodNodeGenerator, List<Method> proxyMethods,
1061                            String owner, int typedTargetIndex, int indexIndex) {
1062    
1063                            _methodNodeGenerator = methodNodeGenerator;
1064                            _proxyMethods = proxyMethods;
1065                            _owner = owner;
1066                            _typedTargetIndex = typedTargetIndex;
1067                            _indexIndex = indexIndex;
1068                    }
1069    
1070                    @Override
1071                    public void generateCase(int key, Label end) {
1072                            Method proxyMethod = _proxyMethods.get(key);
1073    
1074                            Class<?> returnClass = proxyMethod.getReturnType();
1075    
1076                            if (returnClass != void.class) {
1077                                    _methodNodeGenerator.loadThis();
1078    
1079                                    _methodNodeGenerator.loadArg(0);
1080                                    _methodNodeGenerator.loadArg(1);
1081    
1082                                    _methodNodeGenerator.newInstance(_RPC_RESPONSE_TYPE);
1083    
1084                                    _methodNodeGenerator.dup();
1085                            }
1086    
1087                            _methodNodeGenerator.loadLocal(_typedTargetIndex);
1088    
1089                            for (Class<?> parameterClass : proxyMethod.getParameterTypes()) {
1090                                    _methodNodeGenerator.loadArg(2);
1091    
1092                                    Type parameterType = Type.getType(parameterClass);
1093    
1094                                    deserializerRead(_methodNodeGenerator, parameterType);
1095    
1096                                    if (!parameterClass.isPrimitive() &&
1097                                            (parameterClass != Object.class) &&
1098                                            (parameterClass != String.class)) {
1099    
1100                                            _methodNodeGenerator.checkCast(parameterType);
1101                                    }
1102                            }
1103    
1104                            Class<?> declaringClass = proxyMethod.getDeclaringClass();
1105    
1106                            if (declaringClass.isInterface()) {
1107                                    _methodNodeGenerator.invokeInterface(
1108                                            Type.getInternalName(declaringClass), proxyMethod);
1109                            }
1110                            else {
1111                                    _methodNodeGenerator.invokeVirtual(
1112                                            Type.getInternalName(declaringClass), proxyMethod);
1113                            }
1114    
1115                            if (returnClass != void.class) {
1116                                    if (returnClass.isPrimitive()) {
1117                                            _methodNodeGenerator.box(Type.getType(returnClass));
1118                                    }
1119                                    else if (Serializable.class.isAssignableFrom(returnClass)) {
1120                                            _methodNodeGenerator.checkCast(_SERIALIZABLE_TYPE);
1121                                    }
1122    
1123                                    _methodNodeGenerator.invokeSpecial(
1124                                            _RPC_RESPONSE_TYPE.getInternalName(), "<init>",
1125                                            Type.VOID_TYPE, _SERIALIZABLE_TYPE);
1126    
1127                                    _methodNodeGenerator.invokeSpecial(
1128                                            _owner, "_sendResponse", Type.VOID_TYPE,
1129                                            _REGISTRATION_REFERENCE_TYPE, _DATAGRAM_TYPE,
1130                                            _RPC_RESPONSE_TYPE);
1131                            }
1132    
1133                            _methodNodeGenerator.goTo(end);
1134                    }
1135    
1136                    @Override
1137                    public void generateDefault() {
1138                            _methodNodeGenerator.loadThis();
1139    
1140                            _methodNodeGenerator.loadLocal(_indexIndex);
1141    
1142                            _methodNodeGenerator.invokeSpecial(
1143                                    _owner, "_unknownMethodIndex", Type.VOID_TYPE, Type.INT_TYPE);
1144                    }
1145    
1146                    private final int _indexIndex;
1147                    private final MethodNodeGenerator _methodNodeGenerator;
1148                    private final String _owner;
1149                    private final List<Method> _proxyMethods;
1150                    private final int _typedTargetIndex;
1151    
1152            }
1153    
1154    }