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