1 package org.perl.inline.java ; 2 3 import java.lang.reflect.* ; 4 import java.util.* ; 5 import java.io.* ; 6 7 8 public class InlineJavaPerlNatives extends InlineJavaPerlCaller { 9 static private boolean inited = false ; 10 static private Map registered_classes = Collections.synchronizedMap(new HashMap()) ; 11 static private Map registered_methods = Collections.synchronizedMap(new HashMap()) ; 12 13 InlineJavaPerlNatives()14 protected InlineJavaPerlNatives() throws InlineJavaException { 15 init() ; 16 RegisterPerlNatives(this.getClass()) ; 17 } 18 19 init()20 static protected void init() throws InlineJavaException { 21 init("install") ; 22 } 23 24 init(String mode)25 synchronized static protected void init(String mode) throws InlineJavaException { 26 InlineJavaPerlCaller.init() ; 27 if (! inited){ 28 try { 29 String perlnatives_so = GetBundle().getString("inline_java_perlnatives_so_" + mode) ; 30 File f = new File(perlnatives_so) ; 31 if (! f.exists()){ 32 throw new InlineJavaException("Can't initialize PerlNatives " + 33 "functionnality: PerlNatives extension (" + perlnatives_so + 34 ") can't be found") ; 35 } 36 37 try { 38 Class ste_class = Class.forName("java.lang.StackTraceElement") ; 39 } 40 catch (ClassNotFoundException cnfe){ 41 throw new InlineJavaException("Can't initialize PerlNatives " + 42 "functionnality: Java 1.4 or higher required (current is " + 43 System.getProperty("java.version") + ").") ; 44 } 45 46 // Load the Natives shared object 47 InlineJavaUtils.debug(2, "loading shared library " + perlnatives_so) ; 48 System.load(perlnatives_so) ; 49 50 inited = true ; 51 } 52 catch (MissingResourceException mre){ 53 throw new InlineJavaException("Error loading InlineJava.properties resource: " + mre.getMessage()) ; 54 } 55 } 56 } 57 58 59 // This method actually does the real work of registering the methods. RegisterPerlNatives(Class c)60 synchronized private void RegisterPerlNatives(Class c) throws InlineJavaException { 61 if (registered_classes.get(c) == null){ 62 InlineJavaUtils.debug(3, "registering natives for class " + c.getName()) ; 63 64 Constructor constructors[] = c.getDeclaredConstructors() ; 65 Method methods[] = c.getDeclaredMethods() ; 66 67 registered_classes.put(c, c) ; 68 for (int i = 0 ; i < constructors.length ; i++){ 69 Constructor x = constructors[i] ; 70 if (Modifier.isNative(x.getModifiers())){ 71 RegisterMethod(c, "new", x.getParameterTypes(), c) ; 72 } 73 } 74 75 for (int i = 0 ; i < methods.length ; i++){ 76 Method x = methods[i] ; 77 if (Modifier.isNative(x.getModifiers())){ 78 RegisterMethod(c, x.getName(), x.getParameterTypes(), x.getReturnType()) ; 79 } 80 } 81 } 82 } 83 84 RegisterMethod(Class c, String mname, Class params[], Class rt)85 private void RegisterMethod(Class c, String mname, Class params[], Class rt) throws InlineJavaException { 86 String cname = c.getName() ; 87 InlineJavaUtils.debug(3, "registering native method " + mname + " for class " + cname) ; 88 89 // Check return type 90 if ((! Object.class.isAssignableFrom(rt))&&(rt != void.class)){ 91 throw new InlineJavaException("Perl native method " + mname + " of class " + cname + " can only have Object or void return types (not " + rt.getName() + ")") ; 92 } 93 94 // fmt starts with the return type, which for now is Object only (or void). 95 StringBuffer fmt = new StringBuffer("L") ; 96 StringBuffer sign = new StringBuffer("(") ; 97 for (int i = 0 ; i < params.length ; i++){ 98 String code = InlineJavaClass.FindJNICode(params[i]) ; 99 sign.append(code) ; 100 char ch = code.charAt(0) ; 101 char f = ch ; 102 if (f == '['){ 103 // Arrays are Objects... 104 f = 'L' ; 105 } 106 fmt.append(new String(new char [] {f})) ; 107 } 108 sign.append(")") ; 109 110 sign.append(InlineJavaClass.FindJNICode(rt)) ; 111 InlineJavaUtils.debug(3, "signature is " + sign) ; 112 InlineJavaUtils.debug(3, "format is " + fmt) ; 113 114 // For now, no method overloading so no signature necessary 115 String meth = cname + "." + mname ; 116 String prev = (String)registered_methods.get(meth) ; 117 if (prev != null){ 118 throw new InlineJavaException("There already is a native method '" + mname + "' registered for class '" + cname + "'") ; 119 } 120 registered_methods.put(meth, fmt.toString()) ; 121 122 // call the native method to hook it up 123 RegisterMethod(c, mname, sign.toString()) ; 124 } 125 126 127 // This native method will call RegisterNative to hook up the magic 128 // method implementation for the method. RegisterMethod(Class c, String name, String signature)129 native private void RegisterMethod(Class c, String name, String signature) throws InlineJavaException ; 130 131 132 // This method will be called from the native side. We need to figure 133 // out who this method is and then look in up in the 134 // registered method list and return the format. LookupMethod()135 private String LookupMethod() throws InlineJavaException { 136 InlineJavaUtils.debug(3, "entering LookupMethod") ; 137 138 String caller[] = GetNativeCaller() ; 139 String meth = caller[0] + "." + caller[1] ; 140 141 String fmt = (String)registered_methods.get(meth) ; 142 if (fmt == null){ 143 throw new InlineJavaException("Native method " + meth + " is not registered") ; 144 } 145 146 InlineJavaUtils.debug(3, "exiting LookupMethod") ; 147 148 return fmt ; 149 } 150 151 InvokePerlMethod(Object args[])152 private Object InvokePerlMethod(Object args[]) throws InlineJavaException, InlineJavaPerlException { 153 InlineJavaUtils.debug(3, "entering InvokePerlMethod") ; 154 155 String caller[] = GetNativeCaller() ; 156 String pkg = caller[0] ; 157 String method = caller[1] ; 158 159 // Transform the Java class name into the Perl package name 160 StringTokenizer st = new StringTokenizer(pkg, ".") ; 161 StringBuffer perl_sub = new StringBuffer() ; 162 // Starting with "::" means that the package is relative to the caller package 163 while (st.hasMoreTokens()){ 164 perl_sub.append("::" + st.nextToken()) ; 165 } 166 perl_sub.append("::" + method) ; 167 168 for (int i = 0 ; i < args.length ; i++){ 169 InlineJavaUtils.debug(3, "InvokePerlMethod argument " + i + " = " + args[i]) ; 170 } 171 172 Object ret = CallPerlSub(perl_sub.toString(), args) ; 173 174 InlineJavaUtils.debug(3, "exiting InvokePerlMethod") ; 175 176 return ret ; 177 } 178 179 180 // This method must absolutely be called by a method DIRECTLY called 181 // by generic_perl_native GetNativeCaller()182 private String[] GetNativeCaller() throws InlineJavaException { 183 InlineJavaUtils.debug(3, "entering GetNativeCaller") ; 184 185 Class ste_class = null ; 186 try { 187 ste_class = Class.forName("java.lang.StackTraceElement") ; 188 } 189 catch (ClassNotFoundException cnfe){ 190 throw new InlineJavaException("Can't load class java.lang.StackTraceElement") ; 191 } 192 193 Throwable exec_point = new Throwable() ; 194 try { 195 Method m = exec_point.getClass().getMethod("getStackTrace", new Class [] {}) ; 196 Object stack = m.invoke(exec_point, new Object [] {}) ; 197 if (Array.getLength(stack) <= 2){ 198 throw new InlineJavaException("Improper use of InlineJavaPerlNatives.GetNativeCaller (call stack too short)") ; 199 } 200 201 Object ste = Array.get(stack, 2) ; 202 m = ste.getClass().getMethod("isNativeMethod", new Class [] {}) ; 203 Boolean is_nm = (Boolean)m.invoke(ste, new Object [] {}) ; 204 if (! is_nm.booleanValue()){ 205 throw new InlineJavaException("Improper use of InlineJavaPerlNatives.GetNativeCaller (caller is not native)") ; 206 } 207 208 m = ste.getClass().getMethod("getClassName", new Class [] {}) ; 209 String cname = (String)m.invoke(ste, new Object [] {}) ; 210 m = ste.getClass().getMethod("getMethodName", new Class [] {}) ; 211 String mname = (String)m.invoke(ste, new Object [] {}) ; 212 213 InlineJavaUtils.debug(3, "exiting GetNativeCaller") ; 214 215 return new String [] {cname, mname} ; 216 } 217 catch (NoSuchMethodException nsme){ 218 throw new InlineJavaException("Error manipulating java.lang.StackTraceElement classes: " + 219 nsme.getMessage()) ; 220 } 221 catch (IllegalAccessException iae){ 222 throw new InlineJavaException("Error manipulating java.lang.StackTraceElement classes: " + 223 iae.getMessage()) ; 224 } 225 catch (InvocationTargetException ite){ 226 // None of the methods invoked throw exceptions, so... 227 throw new InlineJavaException("Exception caught while manipulating java.lang.StackTraceElement classes: " + 228 ite.getTargetException()) ; 229 } 230 } 231 } 232