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