001
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
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
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
174
175 methodNodeGenerator.loadLocal(serializerIndex);
176
177 methodNodeGenerator.push(skeletonId);
178
179 serializerWrite(methodNodeGenerator, _STRING_TYPE);
180
181
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
192
193 methodNodeGenerator.loadLocal(serializerIndex);
194
195 methodNodeGenerator.push(index);
196
197 serializerWrite(methodNodeGenerator, Type.INT_TYPE);
198
199
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
213
214 methodNodeGenerator.loadThis();
215
216 methodNodeGenerator.loadLocal(serializerIndex);
217
218 Class<?> returnClass = method.getReturnType();
219
220 if (returnClass == void.class) {
221
222
223
224 methodNodeGenerator.invokeSpecial(
225 stubType.getInternalName(), "_send", Type.VOID_TYPE,
226 _SERIALIZER_TYPE);
227
228 methodNodeGenerator.returnValue();
229 }
230 else {
231
232
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
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
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
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
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
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
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
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
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
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
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 }