1 16 17 package org.springframework.scripting.jruby; 18 19 import java.lang.reflect.Array ; 20 import java.lang.reflect.InvocationHandler ; 21 import java.lang.reflect.Method ; 22 import java.lang.reflect.Proxy ; 23 import java.util.Collections ; 24 import java.util.List ; 25 26 import org.jruby.Ruby; 27 import org.jruby.RubyArray; 28 import org.jruby.RubyException; 29 import org.jruby.RubyNil; 30 import org.jruby.ast.ClassNode; 31 import org.jruby.ast.Colon2Node; 32 import org.jruby.ast.NewlineNode; 33 import org.jruby.ast.Node; 34 import org.jruby.exceptions.JumpException; 35 import org.jruby.exceptions.RaiseException; 36 import org.jruby.javasupport.JavaEmbedUtils; 37 import org.jruby.runtime.builtin.IRubyObject; 38 39 import org.springframework.aop.support.AopUtils; 40 import org.springframework.core.NestedRuntimeException; 41 import org.springframework.util.ClassUtils; 42 43 53 public abstract class JRubyScriptUtils { 54 55 64 public static Object createJRubyObject(String scriptSource, Class [] interfaces) throws JumpException { 65 return createJRubyObject(scriptSource, interfaces, ClassUtils.getDefaultClassLoader()); 66 } 67 68 76 public static Object createJRubyObject(String scriptSource, Class [] interfaces, ClassLoader classLoader) { 77 Ruby ruby = initializeRuntime(); 78 79 Node scriptRootNode = ruby.parse(scriptSource, "", null); 80 IRubyObject rubyObject = ruby.eval(scriptRootNode); 81 82 if (rubyObject instanceof RubyNil) { 83 String className = findClassName(scriptRootNode); 84 rubyObject = ruby.evalScript("\n" + className + ".new"); 85 } 86 if (rubyObject instanceof RubyNil) { 88 throw new IllegalStateException ("Compilation of JRuby script returned RubyNil: " + rubyObject); 89 } 90 91 return Proxy.newProxyInstance(classLoader, interfaces, new RubyObjectInvocationHandler(rubyObject, ruby)); 92 } 93 94 97 private static Ruby initializeRuntime() { 98 return JavaEmbedUtils.initialize(Collections.EMPTY_LIST); 99 } 100 101 106 private static String findClassName(Node rootNode) { 107 ClassNode classNode = findClassNode(rootNode); 108 if (classNode == null) { 109 throw new IllegalArgumentException ("Unable to determine class name for root node '" + rootNode + "'"); 110 } 111 Colon2Node node = (Colon2Node) classNode.getCPath(); 112 return node.getName(); 113 } 114 115 120 private static ClassNode findClassNode(Node node) { 121 if (node instanceof ClassNode) { 122 return (ClassNode) node; 123 } 124 List children = node.childNodes(); 125 for (int i = 0; i < children.size(); i++) { 126 Node child = (Node) children.get(i); 127 if (child instanceof ClassNode) { 128 return (ClassNode) child; 129 } else if (child instanceof NewlineNode) { 130 NewlineNode nn = (NewlineNode) child; 131 Node found = findClassNode(nn.getNextNode()); 132 if (found instanceof ClassNode) { 133 return (ClassNode) found; 134 } 135 } 136 } 137 for (int i = 0; i < children.size(); i++) { 138 Node child = (Node) children.get(i); 139 Node found = findClassNode(child); 140 if (found instanceof ClassNode) { 141 return (ClassNode) found; 142 } 143 } 144 return null; 145 } 146 147 148 151 private static class RubyObjectInvocationHandler implements InvocationHandler { 152 153 private final IRubyObject rubyObject; 154 155 private final Ruby ruby; 156 157 public RubyObjectInvocationHandler(IRubyObject rubyObject, Ruby ruby) { 158 this.rubyObject = rubyObject; 159 this.ruby = ruby; 160 } 161 162 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 163 if (AopUtils.isEqualsMethod(method)) { 164 return (isProxyForSameRubyObject(args[0]) ? Boolean.TRUE : Boolean.FALSE); 165 } 166 if (AopUtils.isHashCodeMethod(method)) { 167 return new Integer (this.rubyObject.hashCode()); 168 } 169 if (AopUtils.isToStringMethod(method)) { 170 return "JRuby object [" + this.rubyObject + "]"; 171 } 172 try { 173 IRubyObject[] rubyArgs = convertToRuby(args); 174 IRubyObject rubyResult = 175 this.rubyObject.callMethod(this.ruby.getCurrentContext(), method.getName(), rubyArgs); 176 return convertFromRuby(rubyResult, method.getReturnType()); 177 } 178 catch (RaiseException ex) { 179 throw new JRubyExecutionException(ex); 180 } 181 } 182 183 private boolean isProxyForSameRubyObject(Object other) { 184 if (!Proxy.isProxyClass(other.getClass())) { 185 return false; 186 } 187 InvocationHandler ih = Proxy.getInvocationHandler(other); 188 return (ih instanceof RubyObjectInvocationHandler && 189 this.rubyObject.equals(((RubyObjectInvocationHandler) ih).rubyObject)); 190 } 191 192 private IRubyObject[] convertToRuby(Object [] javaArgs) { 193 if (javaArgs == null || javaArgs.length == 0) { 194 return new IRubyObject[0]; 195 } 196 IRubyObject[] rubyArgs = new IRubyObject[javaArgs.length]; 197 for (int i = 0; i < javaArgs.length; ++i) { 198 rubyArgs[i] = JavaEmbedUtils.javaToRuby(this.ruby, javaArgs[i]); 199 } 200 return rubyArgs; 201 } 202 203 private Object convertFromRuby(IRubyObject rubyResult, Class returnType) { 204 Object result = JavaEmbedUtils.rubyToJava(this.ruby, rubyResult, returnType); 205 if (result instanceof RubyArray && returnType.isArray()) { 206 result = convertFromRubyArray(((RubyArray) result).toJavaArray(), returnType); 207 } 208 return result; 209 } 210 211 private Object convertFromRubyArray(IRubyObject[] rubyArray, Class returnType) { 212 Class targetType = returnType.getComponentType(); 213 Object javaArray = Array.newInstance(targetType, rubyArray.length); 214 for (int i = 0; i < rubyArray.length; i++) { 215 IRubyObject rubyObject = rubyArray[i]; 216 Array.set(javaArray, i, convertFromRuby(rubyObject, targetType)); 217 } 218 return javaArray; 219 } 220 } 221 222 223 230 public static class JRubyExecutionException extends NestedRuntimeException { 231 232 237 public JRubyExecutionException(RaiseException ex) { 238 super(buildMessage(ex), ex); 239 } 240 241 private static String buildMessage(RaiseException ex) { 242 RubyException rubyEx = ex.getException(); 243 return (rubyEx != null && rubyEx.message != null) ? rubyEx.message.toString() : "Unexpected JRuby error"; 244 } 245 } 246 247 } 248 | Popular Tags |