1 28 29 package org.jruby.javasupport.proxy; 30 31 import java.beans.BeanInfo ; 32 import java.beans.IntrospectionException ; 33 import java.beans.Introspector ; 34 import java.beans.PropertyDescriptor ; 35 import java.lang.reflect.Array ; 36 import java.lang.reflect.Constructor ; 37 import java.lang.reflect.InvocationTargetException ; 38 import java.lang.reflect.Method ; 39 import java.lang.reflect.Modifier ; 40 import java.security.AccessController ; 41 import java.security.PrivilegedActionException ; 42 import java.security.PrivilegedExceptionAction ; 43 import java.util.ArrayList ; 44 import java.util.Arrays ; 45 import java.util.HashMap ; 46 import java.util.Iterator ; 47 import java.util.List ; 48 import java.util.Map ; 49 50 import org.jruby.Ruby; 51 import org.jruby.RubyArray; 52 import org.jruby.RubyClass; 53 import org.jruby.RubyFixnum; 54 import org.jruby.RubyModule; 55 import org.jruby.RubyObject; 56 import org.jruby.exceptions.RaiseException; 57 import org.jruby.javasupport.Java; 58 import org.jruby.javasupport.JavaClass; 59 import org.jruby.javasupport.JavaObject; 60 import org.jruby.javasupport.JavaUtil; 61 import org.jruby.runtime.Arity; 62 import org.jruby.runtime.Block; 63 import org.jruby.runtime.CallbackFactory; 64 import org.jruby.runtime.ObjectAllocator; 65 import org.jruby.runtime.builtin.IRubyObject; 66 import org.jruby.runtime.callback.Callback; 67 68 84 public class JavaProxyClass extends JavaProxyReflectionObject { 85 86 static ThreadLocal runtimeTLS = new ThreadLocal (); 87 88 private final Class proxyClass; 89 90 private ArrayList methods = new ArrayList (); 91 92 93 JavaProxyClass(Class proxyClass) { 94 super(getThreadLocalRuntime(), (RubyClass) getThreadLocalRuntime() 95 .getModule("Java").getClass("JavaProxyClass")); 96 this.proxyClass = proxyClass; 97 } 98 99 public Object getValue() { 100 return this; 101 } 102 103 private static Ruby getThreadLocalRuntime() { 104 return (Ruby) runtimeTLS.get(); 105 } 106 107 public static JavaProxyClass getProxyClass(Ruby runtime, Class superClass, 108 Class [] interfaces) throws InvocationTargetException { 109 Object save = runtimeTLS.get(); 110 runtimeTLS.set(runtime); 111 try { 112 ClassLoader loader = runtime.getJavaSupport().getJavaClassLoader(); 113 114 return JavaProxyClassFactory.newProxyClass(loader, null, 115 superClass, interfaces); 116 117 } finally { 118 runtimeTLS.set(save); 119 } 120 } 121 122 public static Object newProxyInstance(Ruby runtime, Class superClass, 123 Class [] interfaces, Class [] constructorParameters, 124 Object [] constructorArgs, JavaProxyInvocationHandler handler) 125 throws IllegalArgumentException , InstantiationException , 126 IllegalAccessException , InvocationTargetException , 127 SecurityException , NoSuchMethodException { 128 JavaProxyClass jpc = getProxyClass(runtime, superClass, interfaces); 129 JavaProxyConstructor cons = jpc 130 .getConstructor(constructorParameters == null ? new Class [0] 131 : constructorParameters); 132 return cons.newInstance(constructorArgs, handler); 133 134 } 135 136 public Class getSuperclass() { 137 return proxyClass.getSuperclass(); 138 } 139 140 public Class [] getInterfaces() { 141 Class [] ifaces = proxyClass.getInterfaces(); 142 Class [] result = new Class [ifaces.length - 1]; 143 int pos = 0; 144 for (int i = 0; i < ifaces.length; i++) { 145 if (ifaces[i] != InternalJavaProxy.class) { 146 result[pos++] = ifaces[i]; 147 } 148 } 149 return result; 150 } 151 152 public JavaProxyConstructor[] getConstructors() { 153 Constructor [] cons = proxyClass.getConstructors(); 154 JavaProxyConstructor[] result = new JavaProxyConstructor[cons.length]; 155 for (int i = 0; i < cons.length; i++) { 156 result[i] = new JavaProxyConstructor(getRuntime(), this, cons[i]); 157 } 158 return result; 159 } 160 161 public JavaProxyConstructor getConstructor(Class [] args) 162 throws SecurityException , NoSuchMethodException { 163 164 Class [] realArgs = new Class [args.length + 1]; 165 System.arraycopy(args, 0, realArgs, 0, args.length); 166 realArgs[args.length] = JavaProxyInvocationHandler.class; 167 168 Constructor constructor = proxyClass.getConstructor(realArgs); 169 return new JavaProxyConstructor(getRuntime(), this, constructor); 170 } 171 172 public JavaProxyMethod[] getMethods() { 173 return (JavaProxyMethod[]) methods.toArray(new JavaProxyMethod[methods 174 .size()]); 175 } 176 177 public JavaProxyMethod getMethod(String name, Class [] parameterTypes) 178 throws NoSuchMethodException { 179 JavaProxyMethod[] all = getMethods(); 180 for (int i = 0; i < all.length; i++) { 181 ProxyMethodImpl jpm = (ProxyMethodImpl) all[i]; 182 if (jpm.matches(name, parameterTypes)) { 183 return jpm; 184 } 185 } 186 throw new NoSuchMethodException (); 187 } 188 189 190 Class getProxyClass() { 191 return proxyClass; 192 } 193 194 public static class ProxyMethodImpl extends JavaProxyReflectionObject 195 implements JavaProxyMethod { 196 private final Method m; 197 198 private Object state; 199 200 private final Method sm; 201 202 private final JavaProxyClass clazz; 203 204 public ProxyMethodImpl(Ruby runtime, JavaProxyClass clazz, Method m, 205 Method sm) { 206 super(runtime, runtime.getModule("Java") 207 .getClass("JavaProxyMethod")); 208 this.m = m; 209 this.sm = sm; 210 this.clazz = clazz; 211 } 212 213 public Method getMethod() { 214 return m; 215 } 216 217 public Method getSuperMethod() { 218 return sm; 219 } 220 221 public int getModifiers() { 222 return m.getModifiers(); 223 } 224 225 public String getName() { 226 return m.getName(); 227 } 228 229 public Class [] getExceptionTypes() { 230 return m.getExceptionTypes(); 231 } 232 233 public Class [] getParameterTypes() { 234 return m.getParameterTypes(); 235 } 236 237 public Object getState() { 238 return state; 239 } 240 241 public boolean hasSuperImplementation() { 242 return sm != null; 243 } 244 245 public Object invoke(Object proxy, Object [] args) 246 throws IllegalArgumentException , IllegalAccessException , 247 InvocationTargetException , NoSuchMethodException { 248 if (!hasSuperImplementation()) { 249 throw new NoSuchMethodException (); 250 } 251 return sm.invoke(proxy, args); 252 } 253 254 public void setState(Object state) { 255 this.state = state; 256 } 257 258 public String toString() { 259 return m.toString(); 260 } 261 262 public Object defaultResult() { 263 Class rt = m.getReturnType(); 264 if (rt == Void.TYPE) { 265 return null; 266 } 267 if (rt == Boolean.TYPE) { 268 return Boolean.FALSE; 269 } 270 if (rt == Byte.TYPE) { 271 return new Byte ((byte) 0); 272 } 273 if (rt == Short.TYPE) { 274 return new Short ((short) 0); 275 } 276 if (rt == Integer.TYPE) { 277 return new Integer (0); 278 } 279 if (rt == Long.TYPE) { 280 return new Long (0L); 281 } 282 if (rt == Float.TYPE) { 283 return new Float (0.0f); 284 } 285 if (rt == Double.TYPE) { 286 return new Double (0.0); 287 } 288 return null; 289 } 290 291 public boolean matches(String name, Class [] parameterTypes) { 292 return m.getName().equals(name) 293 && Arrays.equals(m.getParameterTypes(), parameterTypes); 294 } 295 296 public Class getReturnType() { 297 return m.getReturnType(); 298 } 299 300 public static RubyClass createJavaProxyMethodClass(Ruby runtime, 301 RubyModule javaProxyModule) { 302 303 RubyClass result = javaProxyModule.defineClassUnder("JavaProxyMethod", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR); 304 305 CallbackFactory callbackFactory = runtime 306 .callbackFactory(JavaProxyClass.ProxyMethodImpl.class); 307 308 JavaProxyReflectionObject.registerRubyMethods(runtime, result); 309 310 result.defineFastMethod("argument_types", callbackFactory 311 .getFastMethod("argument_types")); 312 313 result.defineFastMethod("declaring_class", callbackFactory 314 .getFastMethod("getDeclaringClass")); 315 316 result.defineFastMethod("super?", callbackFactory.getFastMethod("super_p")); 317 318 result.defineFastMethod("arity", callbackFactory.getFastMethod("arity")); 319 320 result.defineFastMethod("name", callbackFactory.getFastMethod("name")); 321 322 result 323 .defineFastMethod("inspect", callbackFactory 324 .getFastMethod("inspect")); 325 326 result.defineFastMethod("invoke", callbackFactory 327 .getFastOptMethod("do_invoke")); 328 329 return result; 330 331 } 332 333 public RubyObject name() { 334 return getRuntime().newString(getName()); 335 } 336 337 public JavaProxyClass getDeclaringClass() { 338 return clazz; 339 } 340 341 public RubyArray argument_types() { 342 return buildRubyArray(getParameterTypes()); 343 } 344 345 public IRubyObject super_p() { 346 return hasSuperImplementation() ? getRuntime().getTrue() 347 : getRuntime().getFalse(); 348 } 349 350 public RubyFixnum arity() { 351 return getRuntime().newFixnum(getArity()); 352 } 353 354 protected String nameOnInspection() { 355 return getDeclaringClass().nameOnInspection() + "/" + getName(); 356 } 357 358 public IRubyObject inspect() { 359 StringBuffer result = new StringBuffer (); 360 result.append(nameOnInspection()); 361 result.append("("); 362 Class [] parameterTypes = getParameterTypes(); 363 for (int i = 0; i < parameterTypes.length; i++) { 364 result.append(parameterTypes[i].getName()); 365 if (i < parameterTypes.length - 1) { 366 result.append(','); 367 } 368 } 369 result.append(")>"); 370 return getRuntime().newString(result.toString()); 371 } 372 373 public IRubyObject do_invoke(IRubyObject[] nargs) { 374 if (nargs.length != 1 + getArity()) { 375 throw getRuntime().newArgumentError(nargs.length, 376 1 + getArity()); 377 } 378 379 IRubyObject invokee = nargs[0]; 380 if (!(invokee instanceof JavaObject)) { 381 throw getRuntime().newTypeError("invokee not a java object"); 382 } 383 Object receiver_value = ((JavaObject) invokee).getValue(); 384 Object [] arguments = new Object [nargs.length - 1]; 385 System.arraycopy(nargs, 1, arguments, 0, arguments.length); 386 387 Class [] parameterTypes = getParameterTypes(); 388 for (int i = 0; i < arguments.length; i++) { 389 arguments[i] = JavaUtil.convertRubyToJava( 390 (IRubyObject) arguments[i], parameterTypes[i]); 391 } 392 393 try { 394 Object javaResult = sm.invoke(receiver_value, arguments); 395 return JavaUtil.convertJavaToRuby(getRuntime(), javaResult, 396 getReturnType()); 397 } catch (IllegalArgumentException e) { 398 throw getRuntime().newTypeError( 399 "expected " + argument_types().inspect()); 400 } catch (IllegalAccessException iae) { 401 throw getRuntime().newTypeError( 402 "illegal access on '" + sm.getName() + "': " 403 + iae.getMessage()); 404 } catch (InvocationTargetException ite) { 405 ite.getTargetException().printStackTrace(); 406 getRuntime().getJavaSupport().handleNativeException( 407 ite.getTargetException()); 408 return getRuntime().getNil(); 411 } 412 } 413 414 private int getArity() { 415 return getParameterTypes().length; 416 } 417 418 } 419 420 JavaProxyMethod initMethod(String name, String desc, boolean hasSuper) { 421 Class proxy = proxyClass; 422 try { 423 Class [] parms = parse(proxy.getClassLoader(), desc); 424 Method m = proxy.getDeclaredMethod(name, parms); 425 Method sm = null; 426 if (hasSuper) { 427 sm = proxy.getDeclaredMethod("__super$" + name, parms); 428 } 429 430 JavaProxyMethod jpm = new ProxyMethodImpl(getRuntime(), this, m, sm); 431 methods.add(jpm); 432 return jpm; 433 434 } catch (ClassNotFoundException e) { 435 throw new InternalError (e.getMessage()); 436 } catch (SecurityException e) { 437 throw new InternalError (e.getMessage()); 438 } catch (NoSuchMethodException e) { 439 throw new InternalError (e.getMessage()); 440 } 441 442 } 443 444 private static Class [] parse(final ClassLoader loader, String desc) 445 throws ClassNotFoundException { 446 List al = new ArrayList (); 447 int idx = 1; 448 while (desc.charAt(idx) != ')') { 449 450 int arr = 0; 451 while (desc.charAt(idx) == '[') { 452 idx += 1; 453 arr += 1; 454 } 455 456 Class type; 457 458 switch (desc.charAt(idx)) { 459 case 'L': 460 int semi = desc.indexOf(';', idx); 461 final String name = desc.substring(idx + 1, semi); 462 idx = semi; 463 try { 464 type = (Class ) AccessController 465 .doPrivileged(new PrivilegedExceptionAction () { 466 public Object run() 467 throws ClassNotFoundException { 468 return Class.forName( 469 name.replace('/', '.'), false, 470 loader); 471 } 472 }); 473 } catch (PrivilegedActionException e) { 474 throw (ClassNotFoundException ) e.getException(); 475 } 476 break; 477 478 case 'B': 479 type = Byte.TYPE; 480 break; 481 482 case 'C': 483 type = Character.TYPE; 484 break; 485 486 case 'Z': 487 type = Boolean.TYPE; 488 break; 489 490 case 'S': 491 type = Short.TYPE; 492 break; 493 494 case 'I': 495 type = Integer.TYPE; 496 break; 497 498 case 'J': 499 type = Long.TYPE; 500 break; 501 502 case 'F': 503 type = Float.TYPE; 504 break; 505 506 case 'D': 507 type = Double.TYPE; 508 break; 509 510 default: 511 throw new InternalError ("cannot parse " + desc + "[" + idx 512 + "]"); 513 } 514 515 idx += 1; 516 517 if (arr != 0) { 518 type = Array.newInstance(type, new int[arr]).getClass(); 519 } 520 521 al.add(type); 522 } 523 524 return (Class []) al.toArray(new Class [al.size()]); 525 } 526 527 531 public static RubyClass createJavaProxyClassClass(Ruby runtime, 532 RubyModule javaModule) { 533 RubyClass result = javaModule.defineClassUnder("JavaProxyClass", 534 runtime.getObject(),ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR); 535 536 CallbackFactory callbackFactory = runtime 537 .callbackFactory(JavaProxyClass.class); 538 539 JavaProxyReflectionObject.registerRubyMethods(runtime, result); 540 541 result.defineFastMethod("constructors", callbackFactory 542 .getFastMethod("constructors")); 543 544 result.defineFastMethod("superclass", callbackFactory 545 .getFastMethod("superclass")); 546 547 result.defineFastMethod("interfaces", callbackFactory 548 .getFastMethod("interfaces")); 549 550 result.defineFastMethod("methods", callbackFactory.getFastMethod("methods")); 551 552 result.getMetaClass().defineFastMethod("get", callbackFactory.getFastSingletonMethod( 553 "get", JavaClass.class)); 554 555 result.defineFastMethod("define_instance_methods_for_proxy", 556 callbackFactory.getFastMethod("define_instance_methods_for_proxy", 557 IRubyObject.class)); 558 559 return result; 560 561 } 562 563 public static RubyObject get(IRubyObject recv, JavaClass type) { 564 try { 565 return getProxyClass(recv.getRuntime(), (Class ) type.getValue(), 566 new Class [0]); 567 } catch (Error e) { 568 RaiseException ex = recv.getRuntime().newArgumentError( 569 "unable to create proxy class for " + type.getValue()); 570 ex.initCause(e); 571 throw ex; 572 } catch (InvocationTargetException e) { 573 RaiseException ex = recv.getRuntime().newArgumentError( 574 "unable to create proxy class for " + type.getValue()); 575 ex.initCause(e); 576 throw ex; 577 } 578 } 579 580 public RubyObject superclass() { 581 return JavaClass.get(getRuntime(), getSuperclass()); 582 } 583 584 public RubyArray methods() { 585 return buildRubyArray(getMethods()); 586 } 587 588 public RubyArray interfaces() { 589 Class [] interfaces = getInterfaces(); 590 return buildRubyArray(interfaces); 591 } 592 593 public RubyArray constructors() { 594 return buildRubyArray(getConstructors()); 595 } 596 597 public static void createJavaProxyModule(Ruby runtime) { 598 600 RubyModule javaProxyModule = runtime.getModule("Java"); 601 JavaProxyClass.createJavaProxyClassClass(runtime, javaProxyModule); 602 ProxyMethodImpl.createJavaProxyMethodClass(runtime, javaProxyModule); 603 JavaProxyConstructor.createJavaProxyConstructorClass(runtime, 604 javaProxyModule); 605 606 } 607 608 public String nameOnInspection() { 609 return "[Proxy:" + getSuperclass().getName() + "]"; 610 } 611 612 public IRubyObject define_instance_methods_for_proxy(IRubyObject arg) { 613 assert arg instanceof RubyClass; 614 615 Map aliasesClump = getPropertysClumped(); 616 Map methodsClump = getMethodsClumped(false); 617 RubyClass proxy = (RubyClass) arg; 618 619 for (Iterator iter = methodsClump.keySet().iterator(); iter.hasNext();) { 620 String name = (String ) iter.next(); 621 RubyArray methods = (RubyArray) methodsClump.get(name); 622 List aliases = (List ) aliasesClump.get(name); 623 624 if (aliases == null) { 625 aliases = new ArrayList (); 626 } 627 628 aliases.add(name); 629 630 define_instance_method_for_proxy(proxy, aliases, methods); 631 } 632 633 return getRuntime().getNil(); 634 } 635 636 644 private Map getMethodsClumped(boolean isStatic) { 645 Map map = new HashMap (); 646 JavaProxyMethod[] methods = getMethods(); 647 648 for (int i = 0; i < methods.length; i++) { 649 if (isStatic != Modifier.isStatic(methods[i].getModifiers())) { 650 continue; 651 } 652 653 String key = methods[i].getName(); 654 RubyArray methodsWithName = (RubyArray) map.get(key); 655 656 if (methodsWithName == null) { 657 methodsWithName = getRuntime().newArray(); 658 map.put(key, methodsWithName); 659 } 660 661 methodsWithName.append(methods[i]); 662 } 663 664 return map; 665 } 666 667 private Map getPropertysClumped() { 668 Map map = new HashMap (); 669 BeanInfo info; 670 671 try { 672 info = Introspector.getBeanInfo(proxyClass); 673 } catch (IntrospectionException e) { 674 return map; 675 } 676 677 PropertyDescriptor [] descriptors = info.getPropertyDescriptors(); 678 679 for (int i = 0; i < descriptors.length; i++) { 680 Method readMethod = descriptors[i].getReadMethod(); 681 682 if (readMethod != null) { 683 String key = readMethod.getName(); 684 List aliases = (List ) map.get(key); 685 686 if (aliases == null) { 687 aliases = new ArrayList (); 688 689 map.put(key, aliases); 690 } 691 692 if (readMethod.getReturnType() == Boolean .class 693 || readMethod.getReturnType() == boolean.class) { 694 aliases.add(descriptors[i].getName() + "?"); 695 } 696 aliases.add(descriptors[i].getName()); 697 } 698 699 Method writeMethod = descriptors[i].getWriteMethod(); 700 701 if (writeMethod != null) { 702 String key = writeMethod.getName(); 703 List aliases = (List ) map.get(key); 704 705 if (aliases == null) { 706 aliases = new ArrayList (); 707 map.put(key, aliases); 708 } 709 710 aliases.add(descriptors[i].getName() + "="); 711 } 712 } 713 714 return map; 715 } 716 717 private void define_instance_method_for_proxy(final RubyClass proxy, 718 List names, final RubyArray methods) { 719 final RubyModule javaUtilities = getRuntime() 720 .getModule("JavaUtilities"); 721 Callback method = new Callback() { 722 public IRubyObject execute(IRubyObject self, IRubyObject[] args, Block block) { 723 IRubyObject[] argsArray = new IRubyObject[args.length + 1]; 724 argsArray[0] = self.callMethod(getRuntime().getCurrentContext(),"java_object"); 725 RubyArray argsAsArray = getRuntime().newArray(); 726 for (int j = 0; j < args.length; j++) { 727 argsArray[j + 1] = args[j]; 728 argsAsArray.append(Java.ruby_to_java(proxy, argsArray[j + 1], Block.NULL_BLOCK)); 729 } 730 731 IRubyObject[] mmArgs = new IRubyObject[] { methods, argsAsArray }; 732 IRubyObject result = javaUtilities.callMethod(getRuntime().getCurrentContext(), 733 "matching_method", mmArgs); 734 return Java.java_to_ruby(self, 735 result.callMethod(getRuntime().getCurrentContext(),"invoke", argsArray), 736 Block.NULL_BLOCK); 737 } 738 739 public Arity getArity() { 740 return Arity.optional(); 741 } 742 }; 743 744 for (Iterator iter = names.iterator(); iter.hasNext();) { 745 String methodName = (String ) iter.next(); 746 747 if (!methodName.equals("class")) { 751 proxy.defineMethod(methodName, method); 752 753 String rubyCasedName = JavaClass.getRubyCasedName(methodName); 754 if (rubyCasedName != null) { 755 proxy.defineAlias(rubyCasedName, methodName); 756 } 757 } 758 } 759 } 760 761 } 762 | Popular Tags |