diff options
Diffstat (limited to 'Java/sources/org/perl/inline/java')
20 files changed, 3516 insertions, 0 deletions
diff --git a/Java/sources/org/perl/inline/java/InlineJavaArray.java b/Java/sources/org/perl/inline/java/InlineJavaArray.java new file mode 100644 index 0000000..c397ab4 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaArray.java @@ -0,0 +1,100 @@ +package org.perl.inline.java ; + +import java.util.* ; +import java.lang.reflect.Array ; + + +class InlineJavaArray { + private InlineJavaClass ijc ; + + + InlineJavaArray(InlineJavaClass _ijc){ + ijc = _ijc ; + } + + + Object CreateArray(Class c, StringTokenizer st) throws InlineJavaException { + StringBuffer sb = new StringBuffer(st.nextToken()) ; + sb.replace(0, 1, "") ; + sb.replace(sb.length() - 1, sb.length(), "") ; + + StringTokenizer st2 = new StringTokenizer(sb.toString(), ",") ; + ArrayList al = new ArrayList() ; + while (st2.hasMoreTokens()){ + al.add(al.size(), st2.nextToken()) ; + } + + int size = al.size() ; + int dims[] = new int[size] ; + for (int i = 0 ; i < size ; i++){ + dims[i] = Integer.parseInt((String)al.get(i)) ; + InlineJavaUtils.debug(4, "array dimension: " + (String)al.get(i)) ; + } + + Object array = null ; + try { + array = Array.newInstance(c, dims) ; + + ArrayList args = new ArrayList() ; + while (st.hasMoreTokens()){ + args.add(args.size(), st.nextToken()) ; + } + + // Now we need to fill it. Since we have an arbitrary number + // of dimensions, we can do this recursively. + + PopulateArray(array, c, dims, args) ; + } + catch (IllegalArgumentException e){ + throw new InlineJavaException("Arguments to array constructor for class " + c.getName() + " are incompatible: " + e.getMessage()) ; + } + + return array ; + } + + + void PopulateArray (Object array, Class elem, int dims[], ArrayList args) throws InlineJavaException { + if (dims.length > 1){ + int nb_args = args.size() ; + int nb_sub_dims = dims[0] ; + int nb_args_per_sub_dim = nb_args / nb_sub_dims ; + + int sub_dims[] = new int[dims.length - 1] ; + for (int i = 1 ; i < dims.length ; i++){ + sub_dims[i - 1] = dims[i] ; + } + + for (int i = 0 ; i < nb_sub_dims ; i++){ + // We want the args from i*nb_args_per_sub_dim -> + ArrayList sub_args = new ArrayList() ; + for (int j = (i * nb_args_per_sub_dim) ; j < ((i + 1) * nb_args_per_sub_dim) ; j++){ + sub_args.add(sub_args.size(), (String)args.get(j)) ; + } + PopulateArray(((Object [])array)[i], elem, sub_dims, sub_args) ; + } + } + else{ + String msg = "In creation of array of " + elem.getName() + ": " ; + try { + for (int i = 0 ; i < dims[0] ; i++){ + String arg = (String)args.get(i) ; + + Object o = ijc.CastArgument(elem, arg) ; + Array.set(array, i, o) ; + if (o != null){ + InlineJavaUtils.debug(4, "setting array element " + String.valueOf(i) + " to " + o.toString()) ; + } + else{ + InlineJavaUtils.debug(4, "setting array element " + String.valueOf(i) + " to " + o) ; + } + } + } + catch (InlineJavaCastException e){ + throw new InlineJavaCastException(msg + e.getMessage()) ; + } + catch (InlineJavaException e){ + throw new InlineJavaException(msg + e.getMessage()) ; + } + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaCallback.java b/Java/sources/org/perl/inline/java/InlineJavaCallback.java new file mode 100644 index 0000000..da23afe --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaCallback.java @@ -0,0 +1,158 @@ +package org.perl.inline.java ; + +import java.util.* ; +import java.io.* ; + + +/* + Callback to Perl... +*/ +class InlineJavaCallback { + private InlineJavaServer ijs = InlineJavaServer.GetInstance() ; + private String pkg = null ; + private InlineJavaPerlObject obj = null ; + private String method = null ; + private Object args[] = null ; + private Class cast = null ; + private Object response = null ; + private boolean response_set = false ; + + + InlineJavaCallback(String _pkg, String _method, Object _args[], Class _cast) { + this(null, _pkg, _method, _args, _cast) ; + } + + + InlineJavaCallback(InlineJavaPerlObject _obj, String _method, Object _args[], Class _cast) { + this(_obj, null, _method, _args, _cast) ; + if (obj == null){ + throw new NullPointerException() ; + } + } + + + private InlineJavaCallback(InlineJavaPerlObject _obj, String _pkg, String _method, Object _args[], Class _cast) { + obj = _obj ; + pkg = _pkg ; + method = _method ; + args = _args ; + cast = _cast ; + + if (method == null){ + throw new NullPointerException() ; + } + if (cast == null){ + cast = java.lang.Object.class ; + } + } + + + private String GetCommand(InlineJavaProtocol ijp) throws InlineJavaException { + String via = null ; + if (obj != null){ + via = "" + obj.GetId() ; + } + else if (pkg != null){ + via = pkg ; + } + StringBuffer cmdb = new StringBuffer("callback " + via + " " + method + " " + cast.getName()) ; + if (args != null){ + for (int i = 0 ; i < args.length ; i++){ + cmdb.append(" " + ijp.SerializeObject(args[i], null)) ; + } + } + return cmdb.toString() ; + } + + + void ClearResponse(){ + response = null ; + response_set = false ; + } + + + Object GetResponse(){ + return response ; + } + + + synchronized Object WaitForResponse(Thread t){ + while (! response_set){ + try { + InlineJavaUtils.debug(3, "waiting for callback response in " + t.getName() + "...") ; + wait() ; + } + catch (InterruptedException ie){ + // Do nothing, return and wait() some more... + } + } + InlineJavaUtils.debug(3, "got callback response") ; + Object resp = response ; + response = null ; + response_set = false ; + return resp ; + } + + + synchronized void NotifyOfResponse(Thread t){ + InlineJavaUtils.debug(3, "notifying that callback has completed in " + t.getName()) ; + notify() ; + } + + + synchronized void Process() throws InlineJavaException, InlineJavaPerlException { + Object ret = null ; + try { + InlineJavaProtocol ijp = new InlineJavaProtocol(ijs, null) ; + String cmd = GetCommand(ijp) ; + InlineJavaUtils.debug(2, "callback command: " + cmd) ; + + Thread t = Thread.currentThread() ; + String resp = null ; + while (true) { + InlineJavaUtils.debug(3, "packet sent (callback) is " + cmd) ; + if (! ijs.IsJNI()){ + // Client-server mode. + InlineJavaServerThread ijt = (InlineJavaServerThread)t ; + ijt.GetWriter().write(cmd + "\n") ; + ijt.GetWriter().flush() ; + + resp = ijt.GetReader().readLine() ; + } + else{ + // JNI mode + resp = ijs.jni_callback(cmd) ; + } + InlineJavaUtils.debug(3, "packet recv (callback) is " + resp) ; + + StringTokenizer st = new StringTokenizer(resp, " ") ; + String c = st.nextToken() ; + if (c.equals("callback")){ + boolean thrown = new Boolean(st.nextToken()).booleanValue() ; + String arg = st.nextToken() ; + InlineJavaClass ijc = new InlineJavaClass(ijs, ijp) ; + ret = ijc.CastArgument(cast, arg) ; + + if (thrown){ + throw new InlineJavaPerlException(ret) ; + } + + break ; + } + else{ + // Pass it on through the regular channel... + InlineJavaUtils.debug(3, "packet is not callback response: " + resp) ; + cmd = ijs.ProcessCommand(resp, false) ; + + continue ; + } + } + } + catch (IOException e){ + throw new InlineJavaException("IO error: " + e.getMessage()) ; + } + + response = ret ; + response_set = true ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java b/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java new file mode 100644 index 0000000..a1d7ab1 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java @@ -0,0 +1,141 @@ +package org.perl.inline.java ; + +import java.util.* ; +import java.io.* ; + + +/* + Queue for callbacks to Perl... +*/ +class InlineJavaCallbackQueue { + // private InlineJavaServer ijs = InlineJavaServer.GetInstance() ; + private ArrayList queue = new ArrayList() ; + private boolean wait_interrupted = false ; + private boolean stream_opened = false ; + + + InlineJavaCallbackQueue(){ + } + + + synchronized void EnqueueCallback(InlineJavaCallback ijc){ + queue.add(ijc) ; + notify() ; + } + + + synchronized private InlineJavaCallback DequeueCallback(){ + if (GetSize() > 0){ + return (InlineJavaCallback)queue.remove(0) ; + } + return null ; + } + + + synchronized int WaitForCallback(double timeout){ + long secs = (long)Math.floor(timeout) ; + double rest = timeout - ((double)secs) ; + long millis = (long)Math.floor(rest * 1000.0) ; + rest = (rest * 1000.0) - ((double)millis) ; + int nanos = (int)Math.floor(rest * 1000000.0) ; + + return WaitForCallback((secs * 1000) + millis, nanos) ; + } + + + /* + Blocks up to the specified time for the next callback to arrive. + Returns -1 if the wait was interrupted voluntarily, 0 on timeout or + > 0 if a callback has arrived before the timeout expired. + */ + synchronized int WaitForCallback(long millis, int nanos){ + wait_interrupted = false ; + Thread t = Thread.currentThread() ; + InlineJavaUtils.debug(3, "waiting for callback request (" + millis + " millis, " + + nanos + " nanos) in " + t.getName() + "...") ; + + if (! stream_opened){ + return -1 ; + } + + while ((stream_opened)&&(! wait_interrupted)&&(IsEmpty())){ + try { + wait(millis, nanos) ; + // If we reach this code, it means the either we timed out + // or that we were notify()ed. + // In the former case, we must break out and return 0. + // In the latter case, either the queue will not be empty or + // wait_interrupted will be set. We must therefore also break out. + break ; + } + catch (InterruptedException ie){ + // Do nothing, return and wait() some more... + } + } + InlineJavaUtils.debug(3, "waiting for callback request finished " + t.getName() + "...") ; + + if (wait_interrupted){ + return -1 ; + } + else { + return GetSize() ; + } + } + + + /* + Waits indefinetely for the next callback to arrive and executes it. + Return true on success of false if the wait was interrupted voluntarily. + */ + synchronized boolean ProcessNextCallback() throws InlineJavaException, InlineJavaPerlException { + int rc = WaitForCallback(0, 0) ; + if (rc == -1){ + // Wait was interrupted + return false ; + } + + // DequeueCallback can't return null because we explicetely + // waited until a callback was there. + Thread t = Thread.currentThread() ; + InlineJavaUtils.debug(3, "processing callback request in " + t.getName() + "...") ; + InlineJavaCallback ijc = DequeueCallback() ; + ijc.Process() ; + ijc.NotifyOfResponse(t) ; + + return true ; + } + + + private boolean IsEmpty(){ + return (GetSize() == 0) ; + } + + + void OpenCallbackStream(){ + stream_opened = true ; + } + + + synchronized void CloseCallbackStream(){ + stream_opened = false ; + InterruptWaitForCallback() ; + } + + + boolean IsStreamOpen(){ + return stream_opened ; + } + + + int GetSize(){ + return queue.size() ; + } + + + synchronized private void InterruptWaitForCallback(){ + Thread t = Thread.currentThread() ; + InlineJavaUtils.debug(3, "interrupting wait for callback request in " + t.getName() + "...") ; + wait_interrupted = true ; + notify() ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaCastException.java b/Java/sources/org/perl/inline/java/InlineJavaCastException.java new file mode 100644 index 0000000..b799689 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaCastException.java @@ -0,0 +1,7 @@ +package org.perl.inline.java ; + +class InlineJavaCastException extends InlineJavaException { + InlineJavaCastException(String m){ + super(m) ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaClass.java b/Java/sources/org/perl/inline/java/InlineJavaClass.java new file mode 100644 index 0000000..bd57769 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaClass.java @@ -0,0 +1,554 @@ +package org.perl.inline.java ; + +import java.util.* ; +import java.lang.reflect.* ; + + +class InlineJavaClass { + private InlineJavaServer ijs ; + private InlineJavaProtocol ijp ; + + static private HashMap class2jni_code = new HashMap() ; + static { + class2jni_code.put(byte.class, "B") ; + class2jni_code.put(short.class, "S") ; + class2jni_code.put(int.class, "I") ; + class2jni_code.put(long.class, "J") ; + class2jni_code.put(float.class, "F") ; + class2jni_code.put(double.class, "D") ; + class2jni_code.put(boolean.class, "Z") ; + class2jni_code.put(char.class, "C") ; + class2jni_code.put(void.class, "V") ; + } ; + + static private HashMap class2wrapper = new HashMap() ; + static { + class2wrapper.put(byte.class, java.lang.Byte.class) ; + class2wrapper.put(short.class, java.lang.Short.class) ; + class2wrapper.put(int.class, java.lang.Integer.class) ; + class2wrapper.put(long.class, java.lang.Long.class) ; + class2wrapper.put(float.class, java.lang.Float.class) ; + class2wrapper.put(double.class, java.lang.Double.class) ; + class2wrapper.put(boolean.class, java.lang.Boolean.class) ; + class2wrapper.put(char.class, java.lang.Character.class) ; + class2wrapper.put(void.class, java.lang.Void.class) ; + } ; + + static private HashMap name2class = new HashMap() ; + static { + name2class.put("byte", byte.class) ; + name2class.put("short", short.class) ; + name2class.put("int", int.class) ; + name2class.put("long", long.class) ; + name2class.put("float", float.class) ; + name2class.put("double", double.class) ; + name2class.put("boolean", boolean.class) ; + name2class.put("char", char.class) ; + name2class.put("void", void.class) ; + name2class.put("B", byte.class) ; + name2class.put("S", short.class) ; + name2class.put("I", int.class) ; + name2class.put("J", long.class) ; + name2class.put("F", float.class) ; + name2class.put("D", double.class) ; + name2class.put("Z", boolean.class) ; + name2class.put("C", char.class) ; + name2class.put("V", void.class) ; + } ; + + + InlineJavaClass(InlineJavaServer _ijs, InlineJavaProtocol _ijp){ + ijs = _ijs ; + ijp = _ijp ; + } + + + /* + Makes sure a class exists + */ + static Class ValidateClass(String name) throws InlineJavaException { + Class pc = FindType(name) ; + if (pc != null){ + return pc ; + } + + try { + Class c = Class.forName(name, true, InlineJavaServer.GetInstance().GetUserClassLoader()) ; + return c ; + } + catch (ClassNotFoundException e){ + throw new InlineJavaException("Class " + name + " not found") ; + } + } + + + /* + Remove L...; from a class name if it has been extracted from an Array class name. + */ + static String CleanClassName(String name){ + if (name != null){ + int l = name.length() ; + if ((l > 2)&&(name.charAt(0) == 'L')&&(name.charAt(l - 1) == ';')){ + name = name.substring(1, l - 1) ; + } + } + return name ; + } + + + static private Class ValidateClassQuiet(String name){ + try { + return ValidateClass(name) ; + } + catch (InlineJavaException ije){ + return null ; + } + } + + /* + This is the monster method that determines how to cast arguments + */ + Object [] CastArguments(Class [] params, ArrayList args) throws InlineJavaException { + Object ret[] = new Object [params.length] ; + + for (int i = 0 ; i < params.length ; i++){ + // Here the args are all strings or objects (or undef) + // we need to match them to the prototype. + Class p = params[i] ; + InlineJavaUtils.debug(4, "arg " + String.valueOf(i) + " of signature is " + p.getName()) ; + + ret[i] = CastArgument(p, (String)args.get(i)) ; + } + + return ret ; + } + + + /* + This is the monster method that determines how to cast arguments + */ + Object CastArgument(Class p, String argument) throws InlineJavaException { + Object ret = null ; + + ArrayList tokens = new ArrayList() ; + StringTokenizer st = new StringTokenizer(argument, ":") ; + for (int j = 0 ; st.hasMoreTokens() ; j++){ + tokens.add(j, st.nextToken()) ; + } + if (tokens.size() == 1){ + tokens.add(1, "") ; + } + String type = (String)tokens.get(0) ; + + // We need to separate the primitive types from the + // reference types. + boolean num = ClassIsNumeric(p) ; + if ((num)||(ClassIsString(p))){ + Class ap = p ; + if (ap == java.lang.Number.class){ + InlineJavaUtils.debug(4, "specializing java.lang.Number to java.lang.Double") ; + ap = java.lang.Double.class ; + } + else if (ap.getName().equals("java.lang.CharSequence")){ + InlineJavaUtils.debug(4, "specializing java.lang.CharSequence to java.lang.String") ; + ap = java.lang.String.class ; + } + + if (type.equals("undef")){ + if (num){ + InlineJavaUtils.debug(4, "args is undef -> forcing to " + ap.getName() + " 0") ; + ret = ijp.CreateObject(ap, new Object [] {"0"}, new Class [] {String.class}) ; + InlineJavaUtils.debug(4, " result is " + ret.toString()) ; + } + else{ + ret = null ; + InlineJavaUtils.debug(4, "args is undef -> forcing to " + ap.getName() + " " + ret) ; + InlineJavaUtils.debug(4, " result is " + ret) ; + } + } + else if (type.equals("scalar")){ + String arg = ijp.Decode((String)tokens.get(1)) ; + InlineJavaUtils.debug(4, "args is scalar (" + arg + ") -> forcing to " + ap.getName()) ; + try { + ret = ijp.CreateObject(ap, new Object [] {arg}, new Class [] {String.class}) ; + InlineJavaUtils.debug(4, " result is " + ret.toString()) ; + } + catch (NumberFormatException e){ + throw new InlineJavaCastException("Can't convert " + arg + " to " + ap.getName()) ; + } + } + else if (type.equals("double")){ + String arg = ijp.Decode((String)tokens.get(1)) ; + // We have native double bytes in arg. + long l = 0 ; + char c[] = arg.toCharArray() ; + for (int i = 0 ; i < 8 ; i++){ + l += (((long)c[i]) << (8 * i)) ; + } + double d = Double.longBitsToDouble(l) ; + ret = new Double(d) ; + } + else { + throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ; + } + } + else if (ClassIsBool(p)){ + if (type.equals("undef")){ + InlineJavaUtils.debug(4, "args is undef -> forcing to bool false") ; + ret = new Boolean("false") ; + InlineJavaUtils.debug(4, " result is " + ret.toString()) ; + } + else if (type.equals("scalar")){ + String arg = ijp.Decode((String)tokens.get(1)) ; + InlineJavaUtils.debug(4, "args is scalar (" + arg + ") -> forcing to bool") ; + if ((arg.equals(""))||(arg.equals("0"))){ + arg = "false" ; + } + else{ + arg = "true" ; + } + ret = new Boolean(arg) ; + InlineJavaUtils.debug(4, " result is " + ret.toString()) ; + } + else{ + throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ; + } + } + else if (ClassIsChar(p)){ + if (type.equals("undef")){ + InlineJavaUtils.debug(4, "args is undef -> forcing to char '\0'") ; + ret = new Character('\0') ; + InlineJavaUtils.debug(4, " result is " + ret.toString()) ; + } + else if (type.equals("scalar")){ + String arg = ijp.Decode((String)tokens.get(1)) ; + InlineJavaUtils.debug(4, "args is scalar -> forcing to char") ; + char c = '\0' ; + if (arg.length() == 1){ + c = arg.toCharArray()[0] ; + } + else if (arg.length() > 1){ + throw new InlineJavaCastException("Can't convert " + arg + " to " + p.getName()) ; + } + ret = new Character(c) ; + InlineJavaUtils.debug(4, " result is " + ret.toString()) ; + } + else{ + throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ; + } + } + else { + InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ; + // We know that what we expect here is a real object + if (type.equals("undef")){ + InlineJavaUtils.debug(4, "args is undef -> forcing to null") ; + ret = null ; + } + else if (type.equals("scalar")){ + // Here if we need a java.lang.Object.class, it's probably + // because we can store anything, so we use a String object. + if (p == java.lang.Object.class){ + String arg = ijp.Decode((String)tokens.get(1)) ; + ret = arg ; + } + else{ + throw new InlineJavaCastException("Can't convert primitive type to " + p.getName()) ; + } + } + else if (type.equals("java_object")){ + // We need an object and we got an object... + InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ; + + String c_name = (String)tokens.get(1) ; + String objid = (String)tokens.get(2) ; + + Class c = ValidateClass(c_name) ; + + if (DoesExtend(c, p) > -1){ + InlineJavaUtils.debug(4, " " + c.getName() + " is a kind of " + p.getName()) ; + // get the object from the hash table + int id = Integer.parseInt(objid) ; + Object o = ijs.GetObject(id) ; + ret = o ; + } + else{ + throw new InlineJavaCastException("Can't cast a " + c.getName() + " to a " + p.getName()) ; + } + } + else{ + InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ; + + String pkg = (String)tokens.get(1) ; + pkg = pkg.replace('/', ':') ; + String objid = (String)tokens.get(2) ; + + + if (DoesExtend(p, org.perl.inline.java.InlineJavaPerlObject.class) > -1){ + InlineJavaUtils.debug(4, " Perl object is a kind of " + p.getName()) ; + int id = Integer.parseInt(objid) ; + ret = new InlineJavaPerlObject(pkg, id) ; + } + else{ + throw new InlineJavaCastException("Can't cast a Perl object to a " + p.getName()) ; + } + } + } + + return ret ; + } + + + /* + Returns the number of levels that separate a from b + */ + static int DoesExtend(Class a, Class b){ + return DoesExtend(a, b, 0) ; + } + + + static int DoesExtend(Class a, Class b, int level){ + InlineJavaUtils.debug(4, "checking if " + a.getName() + " extends " + b.getName()) ; + + if (a == b){ + return level ; + } + + Class parent = a.getSuperclass() ; + if (parent != null){ + InlineJavaUtils.debug(4, " parent is " + parent.getName()) ; + int ret = DoesExtend(parent, b, level + 1) ; + if (ret != -1){ + return ret ; + } + } + + // Maybe b is an interface a implements it? + Class inter[] = a.getInterfaces() ; + for (int i = 0 ; i < inter.length ; i++){ + InlineJavaUtils.debug(4, " interface is " + inter[i].getName()) ; + int ret = DoesExtend(inter[i], b, level + 1) ; + if (ret != -1){ + return ret ; + } + } + + return -1 ; + } + + + /* + Finds the wrapper class for the passed primitive type. + */ + static Class FindWrapper(Class p){ + Class w = (Class)class2wrapper.get(p) ; + if (w == null){ + w = p ; + } + + return w ; + } + + + /* + Finds the primitive type class for the passed primitive type name. + */ + static Class FindType (String name){ + return (Class)name2class.get(name) ; + } + + + static String FindJNICode(Class p){ + if (! Object.class.isAssignableFrom(p)){ + return (String)class2jni_code.get(p) ; + } + else { + String name = p.getName().replace('.', '/') ; + if (p.isArray()){ + return name ; + } + else{ + return "L" + name + ";" ; + } + } + } + + + static boolean ClassIsPrimitive(Class p){ + String name = p.getName() ; + + if ((ClassIsNumeric(p))||(ClassIsString(p))||(ClassIsChar(p))||(ClassIsBool(p))){ + InlineJavaUtils.debug(4, "class " + name + " is primitive") ; + return true ; + } + + return false ; + } + + + /* + Determines if class is of numerical type. + */ + static private HashMap numeric_classes = new HashMap() ; + static { + Class [] list = { + java.lang.Byte.class, + java.lang.Short.class, + java.lang.Integer.class, + java.lang.Long.class, + java.lang.Float.class, + java.lang.Double.class, + java.lang.Number.class, + byte.class, + short.class, + int.class, + long.class, + float.class, + double.class, + } ; + for (int i = 0 ; i < list.length ; i++){ + numeric_classes.put(list[i], new Boolean(true)) ; + } + } + static boolean ClassIsNumeric (Class p){ + return (numeric_classes.get(p) != null) ; + } + + + static private HashMap double_classes = new HashMap() ; + static { + Class [] list = { + java.lang.Double.class, + double.class, + } ; + for (int i = 0 ; i < list.length ; i++){ + double_classes.put(list[i], new Boolean(true)) ; + } + } + static boolean ClassIsDouble (Class p){ + return (double_classes.get(p) != null) ; + } + + + /* + Class is String or StringBuffer + */ + static private HashMap string_classes = new HashMap() ; + static { + Class csq = ValidateClassQuiet("java.lang.CharSequence") ; + Class [] list = { + java.lang.String.class, + java.lang.StringBuffer.class, + csq + } ; + for (int i = 0 ; i < list.length ; i++){ + string_classes.put(list[i], new Boolean(true)) ; + } + } + static boolean ClassIsString (Class p){ + return (string_classes.get(p) != null) ; + } + + + /* + Class is Char + */ + static private HashMap char_classes = new HashMap() ; + static { + Class [] list = { + java.lang.Character.class, + char.class, + } ; + for (int i = 0 ; i < list.length ; i++){ + char_classes.put(list[i], new Boolean(true)) ; + } + } + static boolean ClassIsChar (Class p){ + return (char_classes.get(p) != null) ; + } + + + /* + Class is Bool + */ + static private HashMap bool_classes = new HashMap() ; + static { + Class [] list = { + java.lang.Boolean.class, + boolean.class, + } ; + for (int i = 0 ; i < list.length ; i++){ + bool_classes.put(list[i], new Boolean(true)) ; + } + } + static boolean ClassIsBool (Class p){ + return (bool_classes.get(p) != null) ; + } + + + /* + Determines if a class is not of a primitive type or of a + wrapper class. + */ + static boolean ClassIsReference (Class p){ + String name = p.getName() ; + + if (ClassIsPrimitive(p)){ + return false ; + } + + InlineJavaUtils.debug(4, "class " + name + " is reference") ; + + return true ; + } + + + static boolean ClassIsArray (Class p){ + String name = p.getName() ; + + if ((ClassIsReference(p))&&(name.startsWith("["))){ + InlineJavaUtils.debug(4, "class " + name + " is array") ; + return true ; + } + + return false ; + } + + + static boolean ClassIsPublic (Class p){ + int pub = p.getModifiers() & Modifier.PUBLIC ; + if (pub != 0){ + return true ; + } + + return false ; + } + + + static boolean ClassIsHandle (Class p){ + if ((ClassIsReadHandle(p))||(ClassIsWriteHandle(p))){ + return true ; + } + + return false ; + } + + + static boolean ClassIsReadHandle (Class p){ + if ((java.io.Reader.class.isAssignableFrom(p))|| + (java.io.InputStream.class.isAssignableFrom(p))){ + return true ; + } + + return false ; + } + + + static boolean ClassIsWriteHandle (Class p){ + if ((java.io.Writer.class.isAssignableFrom(p))|| + (java.io.OutputStream.class.isAssignableFrom(p))){ + return true ; + } + + return false ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaException.java b/Java/sources/org/perl/inline/java/InlineJavaException.java new file mode 100644 index 0000000..7e2f3cd --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaException.java @@ -0,0 +1,7 @@ +package org.perl.inline.java ; + +public class InlineJavaException extends Exception { + public InlineJavaException(String s) { + super(s) ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaHandle.java b/Java/sources/org/perl/inline/java/InlineJavaHandle.java new file mode 100644 index 0000000..681cf18 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaHandle.java @@ -0,0 +1,130 @@ +package org.perl.inline.java ; + + +import java.util.* ; +import java.io.* ; + + +public class InlineJavaHandle { + private static final String charset = "ISO-8859-1" ; + + + static String read(Object o, int len) throws InlineJavaException, IOException { + String ret = null ; + if (InlineJavaClass.ClassIsReadHandle(o.getClass())){ + if (o instanceof java.io.Reader){ + char buf[] = new char[len] ; + int rc = ((java.io.Reader)o).read(buf) ; + if (rc != -1){ + ret = new String(buf) ; + } + } + else { + byte buf[] = new byte[len] ; + int rc = ((java.io.InputStream)o).read(buf) ; + if (rc != -1){ + ret = new String(buf, charset) ; + } + } + } + else { + throw new InlineJavaException("Can't read from non-readhandle object (" + o.getClass().getName() + ")") ; + } + + return ret ; + } + + + static String readLine(Object o) throws InlineJavaException, IOException { + String ret = null ; + if (InlineJavaClass.ClassIsReadHandle(o.getClass())){ + if (o instanceof java.io.BufferedReader){ + ret = ((java.io.BufferedReader)o).readLine() ; + } + else { + throw new InlineJavaException("Can't read line from non-buffered Reader or InputStream") ; + } + } + else { + throw new InlineJavaException("Can't read line from non-readhandle object (" + o.getClass().getName() + ")") ; + } + + return ret ; + } + + + static Object makeBuffered(Object o) throws InlineJavaException, IOException { + Object ret = null ; + if (InlineJavaClass.ClassIsReadHandle(o.getClass())){ + if (o instanceof java.io.BufferedReader){ + ret = (java.io.BufferedReader)o ; + } + else if (o instanceof java.io.Reader){ + ret = new BufferedReader((java.io.Reader)o) ; + } + else { + ret = new BufferedReader(new InputStreamReader((java.io.InputStream)o, charset)) ; + } + } + else if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){ + if (o instanceof java.io.BufferedWriter){ + ret = (java.io.BufferedWriter)o ; + } + else if (o instanceof java.io.Writer){ + ret = new BufferedWriter((java.io.Writer)o) ; + } + else { + ret = new BufferedWriter(new OutputStreamWriter((java.io.OutputStream)o, charset)) ; + } + } + else { + throw new InlineJavaException("Can't make non-handle object buffered (" + o.getClass().getName() + ")") ; + } + + return ret ; + } + + + static int write(Object o, String str) throws InlineJavaException, IOException { + int ret = -1 ; + if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){ + if (o instanceof java.io.Writer){ + ((java.io.Writer)o).write(str) ; + ret = str.length() ; + } + else { + byte b[] = str.getBytes(charset) ; + ((java.io.OutputStream)o).write(b) ; + ret = b.length ; + } + } + else { + throw new InlineJavaException("Can't write to non-writehandle object (" + o.getClass().getName() + ")") ; + } + + return ret ; + } + + + static void close(Object o) throws InlineJavaException, IOException { + if (InlineJavaClass.ClassIsReadHandle(o.getClass())){ + if (o instanceof java.io.Reader){ + ((java.io.Reader)o).close() ; + } + else { + ((java.io.InputStream)o).close() ; + } + } + else if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){ + if (o instanceof java.io.Writer){ + ((java.io.Writer)o).close() ; + } + else { + ((java.io.OutputStream)o).close() ; + } + } + else { + throw new InlineJavaException("Can't close non-handle object (" + o.getClass().getName() + ")") ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java b/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java new file mode 100644 index 0000000..768bb1a --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java @@ -0,0 +1,15 @@ +package org.perl.inline.java ; + +class InlineJavaInvocationTargetException extends InlineJavaException { + private Throwable t ; + + + InlineJavaInvocationTargetException(String m, Throwable _t){ + super(m) ; + t = _t ; + } + + Throwable GetThrowable(){ + return t ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java b/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java new file mode 100644 index 0000000..c389358 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java @@ -0,0 +1,257 @@ +package org.perl.inline.java ; + +import java.util.* ; +import java.io.* ; + + +/* + Callback to Perl... +*/ +public class InlineJavaPerlCaller { + private InlineJavaServer ijs = InlineJavaServer.GetInstance() ; + private Thread creator = null ; + static private Map thread_callback_queues = Collections.synchronizedMap(new HashMap()) ; + static private ResourceBundle resources = null ; + static private boolean inited = false ; + + + /* + Only thread that communicate with Perl are allowed to create PerlCallers because + this is where we get the thread that needs to be notified when the callbacks come in. + */ + public InlineJavaPerlCaller() throws InlineJavaException { + init() ; + Thread t = Thread.currentThread() ; + if (ijs.IsThreadPerlContact(t)){ + creator = t ; + } + else{ + throw new InlineJavaException("InlineJavaPerlCaller objects can only be created by threads that communicate directly with Perl") ; + } + } + + + synchronized static protected void init() throws InlineJavaException { + if (! inited){ + try { + resources = ResourceBundle.getBundle("InlineJava") ; + + inited = true ; + } + catch (MissingResourceException mre){ + throw new InlineJavaException("Error loading InlineJava.properties: " + mre.getMessage()) ; + } + } + } + + + static protected ResourceBundle GetBundle(){ + return resources ; + } + + /* Old interface */ + /** + * @deprecated As of 0.48, replaced by {@link #CallPerlSub(String,Object[])} + */ + public Object CallPerl(String pkg, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException { + return CallPerl(pkg, method, args, null) ; + } + + + /* Old interface */ + /** + * @deprecated As of 0.48, replaced by {@link #CallPerlSub(String,Object[],Class)} + */ + public Object CallPerl(String pkg, String method, Object args[], String cast) throws InlineJavaException, InlineJavaPerlException { + InlineJavaCallback ijc = new InlineJavaCallback( + (String)null, pkg + "::" + method, args, + (cast == null ? null : InlineJavaClass.ValidateClass(cast))) ; + return CallPerl(ijc) ; + } + + + /* New interface */ + public Object CallPerlSub(String sub, Object args[]) throws InlineJavaException, InlineJavaPerlException { + return CallPerlSub(sub, args, null) ; + } + + + /* New interface */ + public Object CallPerlSub(String sub, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException { + InlineJavaCallback ijc = new InlineJavaCallback( + (String)null, sub, args, cast) ; + return CallPerl(ijc) ; + } + + + /* New interface */ + public Object CallPerlMethod(InlineJavaPerlObject obj, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException { + return CallPerlMethod(obj, method, args, null) ; + } + + + /* New interface */ + public Object CallPerlMethod(InlineJavaPerlObject obj, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException { + InlineJavaCallback ijc = new InlineJavaCallback( + obj, method, args, cast) ; + return CallPerl(ijc) ; + } + + + /* New interface */ + public Object CallPerlStaticMethod(String pkg, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException { + return CallPerlStaticMethod(pkg, method, args, null) ; + } + + + /* New interface */ + public Object CallPerlStaticMethod(String pkg, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException { + InlineJavaCallback ijc = new InlineJavaCallback( + pkg, method, args, cast) ; + return CallPerl(ijc) ; + } + + + public Object eval(String code) throws InlineJavaPerlException, InlineJavaException { + return eval(code, null) ; + } + + + public Object eval(String code, Class cast) throws InlineJavaPerlException, InlineJavaException { + return CallPerlSub("Inline::Java::Callback::java_eval", new Object [] {code}, cast) ; + } + + + public Object require(String module_or_file) throws InlineJavaPerlException, InlineJavaException { + return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {module_or_file}) ; + } + + + public Object require_file(String file) throws InlineJavaPerlException, InlineJavaException { + return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {file, new Boolean("true")}) ; + } + + + public Object require_module(String module) throws InlineJavaPerlException, InlineJavaException { + return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {module, new Boolean("false")}) ; + } + + + private Object CallPerl(InlineJavaCallback ijc) throws InlineJavaException, InlineJavaPerlException { + Thread t = Thread.currentThread() ; + if (t == creator){ + ijc.Process() ; + return ijc.GetResponse() ; + } + else { + // Enqueue the callback into the creator thread's queue and notify it + // that there is some work for him. + ijc.ClearResponse() ; + InlineJavaCallbackQueue q = GetQueue(creator) ; + InlineJavaUtils.debug(3, "enqueing callback for processing for " + creator.getName() + " in " + t.getName() + "...") ; + q.EnqueueCallback(ijc) ; + InlineJavaUtils.debug(3, "notifying that a callback request is available for " + creator.getName() + " in " + t.getName()) ; + + // Now we must wait until the callback is processed and get back the result... + return ijc.WaitForResponse(t) ; + } + } + + + public void OpenCallbackStream() throws InlineJavaException { + Thread t = Thread.currentThread() ; + if (! ijs.IsThreadPerlContact(t)){ + throw new InlineJavaException("InlineJavaPerlCaller.OpenCallbackStream() can only be called by threads that communicate directly with Perl") ; + } + + InlineJavaCallbackQueue q = GetQueue(t) ; + q.OpenCallbackStream() ; + } + + + /* + Blocks until either a callback arrives, timeout seconds has passed or the call is + interrupted by Interrupt? + */ + public int WaitForCallback(double timeout) throws InlineJavaException { + Thread t = Thread.currentThread() ; + if (! ijs.IsThreadPerlContact(t)){ + throw new InlineJavaException("InlineJavaPerlCaller.WaitForCallback() can only be called by threads that communicate directly with Perl") ; + } + + InlineJavaCallbackQueue q = GetQueue(t) ; + if (timeout == 0.0){ + // no wait + return q.GetSize() ; + } + else if (timeout == -1.0){ + timeout = 0.0 ; + } + + return q.WaitForCallback(timeout) ; + } + + + public boolean ProcessNextCallback() throws InlineJavaException, InlineJavaPerlException { + Thread t = Thread.currentThread() ; + if (! ijs.IsThreadPerlContact(t)){ + throw new InlineJavaException("InlineJavaPerlCaller.ProcessNextCallback() can only be called by threads that communicate directly with Perl") ; + } + + InlineJavaCallbackQueue q = GetQueue(t) ; + return q.ProcessNextCallback() ; + } + + + public void CloseCallbackStream() throws InlineJavaException { + InlineJavaCallbackQueue q = GetQueue(creator) ; + q.CloseCallbackStream() ; + } + + + public void StartCallbackLoop() throws InlineJavaException, InlineJavaPerlException { + Thread t = Thread.currentThread() ; + if (! ijs.IsThreadPerlContact(t)){ + throw new InlineJavaException("InlineJavaPerlCaller.StartCallbackLoop() can only be called by threads that communicate directly with Perl") ; + } + + InlineJavaCallbackQueue q = GetQueue(t) ; + InlineJavaUtils.debug(3, "starting callback loop for " + creator.getName() + " in " + t.getName()) ; + q.OpenCallbackStream() ; + while (q.IsStreamOpen()){ + q.ProcessNextCallback() ; + } + } + + + public void StopCallbackLoop() throws InlineJavaException { + Thread t = Thread.currentThread() ; + InlineJavaCallbackQueue q = GetQueue(creator) ; + InlineJavaUtils.debug(3, "stopping callback loop for " + creator.getName() + " in " + t.getName()) ; + q.CloseCallbackStream() ; + } + + + /* + Here the prototype accepts Threads because the JNI thread + calls this method also. + */ + static synchronized void AddThread(Thread t){ + thread_callback_queues.put(t, new InlineJavaCallbackQueue()) ; + } + + + static synchronized void RemoveThread(InlineJavaServerThread t){ + thread_callback_queues.remove(t) ; + } + + + static private InlineJavaCallbackQueue GetQueue(Thread t) throws InlineJavaException { + InlineJavaCallbackQueue q = (InlineJavaCallbackQueue)thread_callback_queues.get(t) ; + + if (q == null){ + throw new InlineJavaException("Can't find thread " + t.getName() + "!") ; + } + return q ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlException.java b/Java/sources/org/perl/inline/java/InlineJavaPerlException.java new file mode 100644 index 0000000..38fd631 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaPerlException.java @@ -0,0 +1,25 @@ +package org.perl.inline.java ; + + +public class InlineJavaPerlException extends Exception { + private Object obj ; + + + public InlineJavaPerlException(Object o){ + super(o.toString()) ; + obj = o ; + } + + public InlineJavaPerlException(String s){ + super(s) ; + obj = s ; + } + + public Object GetObject(){ + return obj ; + } + + public String GetString(){ + return (String)obj ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java b/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java new file mode 100644 index 0000000..6a24ce2 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java @@ -0,0 +1,107 @@ +package org.perl.inline.java ; + + +import java.util.* ; +import java.io.* ; + + +/* + InlineJavaPerlInterpreter + + This singleton class creates a PerlInterpreter object. To this object is bound + an instance of InlineJavaServer that will allow communication with Perl. + + All communication with Perl must be done via InlineJavaPerlCaller in order to insure + thread synchronization. Therefore all Perl actions will be implemented via functions + in Inline::Java::PerlInterperter so that they can be called via InlineJavaPerlCaller +*/ +public class InlineJavaPerlInterpreter extends InlineJavaPerlCaller { + static private boolean inited = false ; + static InlineJavaPerlInterpreter instance = null ; + static boolean test = false ; + static String libperl_so = "" ; + + + protected InlineJavaPerlInterpreter() throws InlineJavaPerlException, InlineJavaException { + init() ; + + InlineJavaUtils.debug(2, "constructing perl interpreter") ; + construct() ; + InlineJavaUtils.debug(2, "perl interpreter constructed") ; + + if (! libperl_so.equals("")){ + evalNoReturn("require DynaLoader ;") ; + evalNoReturn("DynaLoader::dl_load_file(\"" + libperl_so + "\", 0x01) ;") ; + } + if (test){ + evalNoReturn("use blib ;") ; + } + evalNoReturn("use Inline::Java::PerlInterpreter ;") ; + } + + + synchronized static public InlineJavaPerlInterpreter create() throws InlineJavaPerlException, InlineJavaException { + if (instance == null){ + // Here we create a temporary InlineJavaServer instance in order to be able to instanciate + // ourselves. When we create InlineJavaPerlInterpreter, the instance will be overriden. + InlineJavaUtils.debug(2, "creating temporary JNI InlineJavaServer") ; + InlineJavaServer.jni_main(InlineJavaUtils.get_debug(), false) ; + InlineJavaUtils.debug(2, "temporary JNI InlineJavaServer created") ; + InlineJavaUtils.debug(2, "creating InlineJavaPerlInterpreter") ; + instance = new InlineJavaPerlInterpreter() ; + InlineJavaUtils.debug(2, "InlineJavaPerlInterpreter created") ; + } + return instance ; + } + + + synchronized static protected void init() throws InlineJavaException { + init("install") ; + } + + + synchronized static protected void init(String mode) throws InlineJavaException { + InlineJavaPerlCaller.init() ; + if (! inited){ + test = (mode.equals("test") ? true : false) ; + try { + String perlinterpreter_so = GetBundle().getString("inline_java_perlinterpreter_so_" + mode) ; + File f = new File(perlinterpreter_so) ; + if (! f.exists()){ + throw new InlineJavaException("Can't initialize PerlInterpreter " + + "functionnality: PerlInterpreter extension (" + perlinterpreter_so + + ") can't be found") ; + } + + // Load the PerlInterpreter shared object + InlineJavaUtils.debug(2, "loading shared library " + perlinterpreter_so) ; + System.load(perlinterpreter_so) ; + InlineJavaUtils.debug(2, "shared library " + perlinterpreter_so + " loaded") ; + + libperl_so = GetBundle().getString("inline_java_libperl_so") ; + + inited = true ; + } + catch (MissingResourceException mre){ + throw new InlineJavaException("Error loading InlineJava.properties resource: " + mre.getMessage()) ; + } + } + } + + + synchronized static private native void construct() ; + + + synchronized static private native void evalNoReturn(String code) throws InlineJavaPerlException ; + + + synchronized static private native void destruct() ; + + + synchronized static public void destroy() { + if (instance != null){ + destruct() ; + instance = null ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java b/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java new file mode 100644 index 0000000..5682e6a --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java @@ -0,0 +1,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 registered_classes = Collections.synchronizedMap(new HashMap()) ; + static private Map registered_methods = Collections.synchronizedMap(new HashMap()) ; + + + 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()) ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java b/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java new file mode 100644 index 0000000..b57caab --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java @@ -0,0 +1,73 @@ +package org.perl.inline.java ; + + +/* + InlineJavaPerlObject +*/ +public class InlineJavaPerlObject extends InlineJavaPerlCaller { + private int id = 0 ; + private String pkg = null ; + + + /* + Creates a Perl Object by calling + pkg->new(args) ; + */ + public InlineJavaPerlObject(String _pkg, Object args[]) throws InlineJavaPerlException, InlineJavaException { + pkg = _pkg ; + InlineJavaPerlObject stub = (InlineJavaPerlObject)CallPerlStaticMethod(pkg, "new", args, getClass()) ; + id = stub.GetId() ; + stub.id = 0 ; + } + + + /* + This is just a stub for already existing objects + */ + InlineJavaPerlObject(String _pkg, int _id) throws InlineJavaException { + pkg = _pkg ; + id = _id ; + } + + + int GetId(){ + return id ; + } + + + public String GetPkg(){ + return pkg ; + } + + + public Object InvokeMethod(String name, Object args[]) throws InlineJavaPerlException, InlineJavaException { + return InvokeMethod(name, args, null) ; + } + + + public Object InvokeMethod(String name, Object args[], Class cast) throws InlineJavaPerlException, InlineJavaException { + return CallPerlMethod(this, name, args, cast) ; + } + + + public void Dispose() throws InlineJavaPerlException, InlineJavaException { + Dispose(false) ; + } + + + protected void Dispose(boolean gc) throws InlineJavaPerlException, InlineJavaException { + if (id != 0){ + CallPerlSub("Inline::Java::Callback::java_finalize", new Object [] {new Integer(id), new Boolean(gc)}) ; + } + } + + + protected void finalize() throws Throwable { + try { + Dispose(true) ; + } + finally { + super.finalize() ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaProtocol.java b/Java/sources/org/perl/inline/java/InlineJavaProtocol.java new file mode 100644 index 0000000..e84c214 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaProtocol.java @@ -0,0 +1,879 @@ +package org.perl.inline.java ; + +import java.util.* ; +import java.io.* ; +import java.lang.reflect.* ; + + +/* + This is where most of the work of Inline Java is done. Here determine + the request type and then we proceed to serve it. +*/ +class InlineJavaProtocol { + private InlineJavaServer ijs ; + private InlineJavaClass ijc ; + private InlineJavaArray ija ; + private String cmd ; + private String response = null ; + + private final String encoding = "UTF-8" ; + + static private Map member_cache = Collections.synchronizedMap(new HashMap()) ; + static private final String report_version = "V2" ; + + InlineJavaProtocol(InlineJavaServer _ijs, String _cmd) { + ijs = _ijs ; + ijc = new InlineJavaClass(ijs, this) ; + ija = new InlineJavaArray(ijc) ; + + cmd = _cmd ; + } + + + /* + Starts the analysis of the command line + */ + void Do() throws InlineJavaException { + StringTokenizer st = new StringTokenizer(cmd, " ") ; + String c = st.nextToken() ; + + if (c.equals("call_method")){ + CallJavaMethod(st) ; + } + else if (c.equals("set_member")){ + SetJavaMember(st) ; + } + else if (c.equals("get_member")){ + GetJavaMember(st) ; + } + else if (c.equals("add_classpath")){ + AddClassPath(st) ; + } + else if (c.equals("server_type")){ + ServerType(st) ; + } + else if (c.equals("report")){ + Report(st) ; + } + else if (c.equals("isa")){ + IsA(st) ; + } + else if (c.equals("create_object")){ + CreateJavaObject(st) ; + } + else if (c.equals("delete_object")){ + DeleteJavaObject(st) ; + } + else if (c.equals("obj_cnt")){ + ObjectCount(st) ; + } + else if (c.equals("cast")){ + Cast(st) ; + } + else if (c.equals("read")){ + Read(st) ; + } + else if (c.equals("make_buffered")){ + MakeBuffered(st) ; + } + else if (c.equals("readline")){ + ReadLine(st) ; + } + else if (c.equals("write")){ + Write(st) ; + } + else if (c.equals("close")){ + Close(st) ; + } + else if (c.equals("die")){ + InlineJavaUtils.debug(1, "received a request to die...") ; + ijs.Shutdown() ; + } + else { + throw new InlineJavaException("Unknown command " + c) ; + } + } + + /* + Returns a report on the Java classes, listing all public methods + and members + */ + void Report(StringTokenizer st) throws InlineJavaException { + StringBuffer pw = new StringBuffer(report_version + "\n") ; + + StringTokenizer st2 = new StringTokenizer(st.nextToken(), ":") ; + st2.nextToken() ; + + StringTokenizer st3 = new StringTokenizer(Decode(st2.nextToken()), " ") ; + + ArrayList class_list = new ArrayList() ; + while (st3.hasMoreTokens()){ + String c = st3.nextToken() ; + class_list.add(class_list.size(), c) ; + } + + for (int i = 0 ; i < class_list.size() ; i++){ + String name = (String)class_list.get(i) ; + Class c = ijc.ValidateClass(name) ; + + InlineJavaUtils.debug(3, "reporting for " + c) ; + + Class parent = c.getSuperclass() ; + String pname = (parent == null ? "null" : parent.getName()) ; + pw.append("class " + c.getName() + " " + pname + "\n") ; + Constructor constructors[] = c.getConstructors() ; + Method methods[] = c.getMethods() ; + Field fields[] = c.getFields() ; + + boolean pub = ijc.ClassIsPublic(c) ; + if (pub){ + // If the class is public and has no constructors, + // we provide a default no-arg constructors. + if (c.getDeclaredConstructors().length == 0){ + String noarg_sign = InlineJavaUtils.CreateSignature(new Class [] {}) ; + pw.append("constructor " + noarg_sign + "\n") ; + } + } + + boolean pn = InlineJavaPerlNatives.class.isAssignableFrom(c) ; + for (int j = 0 ; j < constructors.length ; j++){ + Constructor x = constructors[j] ; + if ((pn)&&(Modifier.isNative(x.getModifiers()))){ + continue ; + } + Class params[] = x.getParameterTypes() ; + String sign = InlineJavaUtils.CreateSignature(params) ; + Class decl = x.getDeclaringClass() ; + pw.append("constructor " + sign + "\n") ; + } + + for (int j = 0 ; j < methods.length ; j++){ + Method x = methods[j] ; + if ((pn)&&(Modifier.isNative(x.getModifiers()))){ + continue ; + } + String stat = (Modifier.isStatic(x.getModifiers()) ? " static " : " instance ") ; + String sign = InlineJavaUtils.CreateSignature(x.getParameterTypes()) ; + Class decl = x.getDeclaringClass() ; + pw.append("method" + stat + decl.getName() + " " + x.getName() + sign + "\n") ; + } + + for (int j = 0 ; j < fields.length ; j++){ + Field x = fields[(InlineJavaUtils.ReverseMembers() ? (fields.length - 1 - j) : j)] ; + String stat = (Modifier.isStatic(x.getModifiers()) ? " static " : " instance ") ; + Class decl = x.getDeclaringClass() ; + Class type = x.getType() ; + pw.append("field" + stat + decl.getName() + " " + x.getName() + " " + type.getName() + "\n") ; + } + } + + SetResponse(pw.toString()) ; + } + + + void AddClassPath(StringTokenizer st) throws InlineJavaException { + while (st.hasMoreTokens()){ + String path = Decode(st.nextToken()) ; + InlineJavaServer.GetInstance().GetUserClassLoader().AddClassPath(path) ; + } + SetResponse(null) ; + } + + + void ServerType(StringTokenizer st) throws InlineJavaException { + SetResponse(ijs.GetType()) ; + } + + + void IsA(StringTokenizer st) throws InlineJavaException { + String class_name = st.nextToken() ; + Class c = ijc.ValidateClass(class_name) ; + + String is_it_a = st.nextToken() ; + Class d = ijc.ValidateClass(is_it_a) ; + + SetResponse(new Integer(ijc.DoesExtend(c, d))) ; + } + + + void ObjectCount(StringTokenizer st) throws InlineJavaException { + SetResponse(new Integer(ijs.ObjectCount())) ; + } + + + /* + Creates a Java Object with the specified arguments. + */ + void CreateJavaObject(StringTokenizer st) throws InlineJavaException { + String class_name = st.nextToken() ; + Class c = ijc.ValidateClass(class_name) ; + + if (! ijc.ClassIsArray(c)){ + ArrayList f = ValidateMethod(true, c, class_name, st) ; + Object p[] = (Object [])f.get(1) ; + Class clist[] = (Class [])f.get(2) ; + + try { + Object o = CreateObject(c, p, clist) ; + SetResponse(o) ; + } + catch (InlineJavaInvocationTargetException ite){ + Throwable t = ite.GetThrowable() ; + if (t instanceof InlineJavaException){ + InlineJavaException ije = (InlineJavaException)t ; + throw ije ; + } + else{ + SetResponse(new InlineJavaThrown(t)) ; + } + } + } + else{ + // Here we send the type of array we want, but CreateArray + // exception the element type. + StringBuffer sb = new StringBuffer(class_name) ; + // Remove the ['s + while (sb.toString().startsWith("[")){ + sb.replace(0, 1, "") ; + } + // remove the L and the ; + if (sb.toString().startsWith("L")){ + sb.replace(0, 1, "") ; + sb.replace(sb.length() - 1, sb.length(), "") ; + } + + Class ec = ijc.ValidateClass(sb.toString()) ; + + InlineJavaUtils.debug(4, "array elements: " + ec.getName()) ; + Object o = ija.CreateArray(ec, st) ; + SetResponse(o) ; + } + } + + + /* + Calls a Java method + */ + void CallJavaMethod(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + String class_name = st.nextToken() ; + Object o = null ; + if (id > 0){ + o = ijs.GetObject(id) ; + + // Use the class sent by Perl (it might be casted) + // class_name = o.getClass().getName() ; + } + + Class c = ijc.ValidateClass(class_name) ; + String method = st.nextToken() ; + + if ((ijc.ClassIsArray(c))&&(method.equals("getLength"))){ + int length = Array.getLength(o) ; + SetResponse(new Integer(length)) ; + } + else{ + ArrayList f = ValidateMethod(false, c, method, st) ; + Method m = (Method)f.get(0) ; + String name = m.getName() ; + Object p[] = (Object [])f.get(1) ; + + try { + Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().invoke(m, o, p) ; + SetResponse(ret, AutoCast(ret, m.getReturnType())) ; + } + catch (IllegalAccessException e){ + throw new InlineJavaException("You are not allowed to invoke method " + name + " in class " + class_name + ": " + e.getMessage()) ; + } + catch (IllegalArgumentException e){ + throw new InlineJavaException("Arguments for method " + name + " in class " + class_name + " are incompatible: " + e.getMessage()) ; + } + catch (InvocationTargetException e){ + Throwable t = e.getTargetException() ; + String type = t.getClass().getName() ; + String msg = t.getMessage() ; + InlineJavaUtils.debug(1, "method " + name + " in class " + class_name + " threw exception " + type + ": " + msg) ; + if (t instanceof InlineJavaException){ + InlineJavaException ije = (InlineJavaException)t ; + throw ije ; + } + else{ + SetResponse(new InlineJavaThrown(t)) ; + } + } + } + } + + + /* + */ + Class AutoCast(Object o, Class want){ + if (o == null){ + return null ; + } + else { + Class got = o.getClass() ; + if (got.equals(want)){ + return null ; + } + else { + boolean _public = (got.getModifiers() & Modifier.PUBLIC) != 0 ; + if ((_public)||(got.getPackage() == null)){ + return null ; + } + else { + InlineJavaUtils.debug(3, "AutoCast: " + got.getName() + " -> " + want.getName()) ; + return want ; + } + } + } + } + + + /* + Returns a new reference to the current object, using the provided subtype + */ + void Cast(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + String class_name = st.nextToken() ; + Object o = ijs.GetObject(id) ; + Class c = ijc.ValidateClass(class_name) ; + + SetResponse(o, c) ; + } + + + /* + */ + void Read(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + int len = Integer.parseInt(st.nextToken()) ; + + Object o = ijs.GetObject(id) ; + Object ret = null ; + try { + ret = InlineJavaHandle.read(o, len) ; + } + catch (java.io.IOException e){ + ret = new InlineJavaThrown(e) ; + } + + SetResponse(ret) ; + } + + + void MakeBuffered(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + Object o = ijs.GetObject(id) ; + Object ret = null ; + try { + ret = InlineJavaHandle.makeBuffered(o) ; + if (ret != o){ + int buf_id = ijs.PutObject(ret) ; + ret = new Integer(buf_id) ; + } + else { + ret = new Integer(id) ; + } + } + catch (java.io.IOException e){ + ret = new InlineJavaThrown(e) ; + } + + SetResponse(ret) ; + } + + + void ReadLine(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + Object o = ijs.GetObject(id) ; + Object ret = null ; + try { + ret = InlineJavaHandle.readLine(o) ; + } + catch (java.io.IOException e){ + ret = new InlineJavaThrown(e) ; + } + + SetResponse(ret) ; + } + + + void Write(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + Object arg = ijc.CastArgument(Object.class, st.nextToken()) ; + + Object o = ijs.GetObject(id) ; + Object ret = null ; + try { + int len = InlineJavaHandle.write(o, arg.toString()) ; + ret = new Integer(len) ; + } + catch (java.io.IOException e){ + ret = new InlineJavaThrown(e) ; + } + + SetResponse(ret) ; + } + + + void Close(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + Object o = ijs.GetObject(id) ; + Object ret = null ; + try { + InlineJavaHandle.close(o) ; + } + catch (java.io.IOException e){ + ret = new InlineJavaThrown(e) ; + } + + SetResponse(ret) ; + } + + + /* + Sets a Java member variable + */ + void SetJavaMember(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + String class_name = st.nextToken() ; + Object o = null ; + if (id > 0){ + o = ijs.GetObject(id) ; + + // Use the class sent by Perl (it might be casted) + // class_name = o.getClass().getName() ; + } + + Class c = ijc.ValidateClass(class_name) ; + String member = st.nextToken() ; + + if (ijc.ClassIsArray(c)){ + int idx = Integer.parseInt(member) ; + Class type = ijc.ValidateClass(st.nextToken()) ; + String arg = st.nextToken() ; + + String msg = "For array of type " + c.getName() + ", element " + member + ": " ; + try { + Object elem = ijc.CastArgument(type, arg) ; + InlineJavaServer.GetInstance().GetUserClassLoader().array_set(o, idx, elem) ; + SetResponse(null) ; + } + catch (InlineJavaCastException e){ + throw new InlineJavaCastException(msg + e.getMessage()) ; + } + catch (InlineJavaException e){ + throw new InlineJavaException(msg + e.getMessage()) ; + } + } + else{ + ArrayList fl = ValidateMember(c, member, st) ; + Field f = (Field)fl.get(0) ; + String name = f.getName() ; + Object p = (Object)fl.get(1) ; + + try { + InlineJavaServer.GetInstance().GetUserClassLoader().set(f, o, p) ; + SetResponse(null) ; + } + catch (IllegalAccessException e){ + throw new InlineJavaException("You are not allowed to set member " + name + " in class " + class_name + ": " + e.getMessage()) ; + } + catch (IllegalArgumentException e){ + throw new InlineJavaException("Argument for member " + name + " in class " + class_name + " is incompatible: " + e.getMessage()) ; + } + } + } + + + /* + Gets a Java member variable + */ + void GetJavaMember(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + String class_name = st.nextToken() ; + Object o = null ; + if (id > 0){ + o = ijs.GetObject(id) ; + + // Use the class sent by Perl (it might be casted) + // class_name = o.getClass().getName() ; + } + + Class c = ijc.ValidateClass(class_name) ; + String member = st.nextToken() ; + + if (ijc.ClassIsArray(c)){ + int idx = Integer.parseInt(member) ; + Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().array_get(o, idx) ; + Class eclass = ijc.ValidateClass(ijc.CleanClassName(class_name.substring(1))) ; + SetResponse(ret, AutoCast(ret, eclass)) ; + } + else{ + ArrayList fl = ValidateMember(c, member, st) ; + + Field f = (Field)fl.get(0) ; + String name = f.getName() ; + try { + Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().get(f, o) ; + SetResponse(ret, AutoCast(ret, f.getType())) ; + } + catch (IllegalAccessException e){ + throw new InlineJavaException("You are not allowed to set member " + name + " in class " + class_name + ": " + e.getMessage()) ; + } + catch (IllegalArgumentException e){ + throw new InlineJavaException("Argument for member " + name + " in class " + class_name + " is incompatible: " + e.getMessage()) ; + } + } + } + + + /* + Deletes a Java object + */ + void DeleteJavaObject(StringTokenizer st) throws InlineJavaException { + int id = Integer.parseInt(st.nextToken()) ; + + Object o = ijs.DeleteObject(id) ; + + SetResponse(null) ; + } + + + /* + Creates a Java Object with the specified arguments. + */ + Object CreateObject(Class p, Object args[], Class proto[]) throws InlineJavaException { + p = ijc.FindWrapper(p) ; + + String name = p.getName() ; + Object ret = null ; + try { + ret = InlineJavaServer.GetInstance().GetUserClassLoader().create(p, args, proto) ; + } + catch (NoSuchMethodException e){ + throw new InlineJavaException("Constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " not found: " + e.getMessage()) ; + } + catch (InstantiationException e){ + throw new InlineJavaException("You are not allowed to instantiate object of class " + name + ": " + e.getMessage()) ; + } + catch (IllegalAccessException e){ + throw new InlineJavaException("You are not allowed to instantiate object of class " + name + " using the constructor with signature " + InlineJavaUtils.CreateSignature(proto) + ": " + e.getMessage()) ; + } + catch (IllegalArgumentException e){ + throw new InlineJavaException("Arguments to constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " are incompatible: " + e.getMessage()) ; + } + catch (InvocationTargetException e){ + Throwable t = e.getTargetException() ; + String type = t.getClass().getName() ; + String msg = t.getMessage() ; + throw new InlineJavaInvocationTargetException( + "Constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " threw exception " + type + ": " + msg, + t) ; + } + + return ret ; + } + + + /* + Makes sure a method exists + */ + ArrayList ValidateMethod(boolean constructor, Class c, String name, StringTokenizer st) throws InlineJavaException { + ArrayList ret = new ArrayList() ; + + // Extract signature + String signature = st.nextToken() ; + + // Extract the arguments + ArrayList args = new ArrayList() ; + while (st.hasMoreTokens()){ + args.add(args.size(), st.nextToken()) ; + } + + String key = c.getName() + "." + name + signature ; + ArrayList ml = new ArrayList() ; + Class params[] = null ; + + Member cached = (Member)member_cache.get(key) ; + if (cached != null){ + InlineJavaUtils.debug(3, "method was cached") ; + ml.add(ml.size(), cached) ; + } + else{ + Member ma[] = (constructor ? (Member [])c.getConstructors() : (Member [])c.getMethods()) ; + for (int i = 0 ; i < ma.length ; i++){ + Member m = ma[i] ; + + if (m.getName().equals(name)){ + InlineJavaUtils.debug(3, "found a " + name + (constructor ? " constructor" : " method")) ; + + if (constructor){ + params = ((Constructor)m).getParameterTypes() ; + } + else{ + params = ((Method)m).getParameterTypes() ; + } + + // Now we check if the signatures match + String sign = InlineJavaUtils.CreateSignature(params, ",") ; + InlineJavaUtils.debug(3, sign + " = " + signature + "?") ; + + if (signature.equals(sign)){ + InlineJavaUtils.debug(3, "has matching signature " + sign) ; + ml.add(ml.size(), m) ; + member_cache.put(key, m) ; + break ; + } + } + } + } + + // Now we got a list of matching methods (actually 0 or 1). + // We have to figure out which one we will call. + if (ml.size() == 0){ + // Nothing matched. Maybe we got a default constructor + if ((constructor)&&(signature.equals("()"))){ + ret.add(0, null) ; + ret.add(1, new Object [] {}) ; + ret.add(2, new Class [] {}) ; + } + else{ + throw new InlineJavaException( + (constructor ? "Constructor " : "Method ") + + name + " for class " + c.getName() + " with signature " + + signature + " not found") ; + } + } + else if (ml.size() == 1){ + // Now we need to force the arguments received to match + // the methods signature. + Member m = (Member)ml.get(0) ; + if (constructor){ + params = ((Constructor)m).getParameterTypes() ; + } + else{ + params = ((Method)m).getParameterTypes() ; + } + + String msg = "In method " + name + " of class " + c.getName() + ": " ; + try { + ret.add(0, m) ; + ret.add(1, ijc.CastArguments(params, args)) ; + ret.add(2, params) ; + } + catch (InlineJavaCastException e){ + throw new InlineJavaCastException(msg + e.getMessage()) ; + } + catch (InlineJavaException e){ + throw new InlineJavaException(msg + e.getMessage()) ; + } + } + + return ret ; + } + + + /* + Makes sure a member exists + */ + ArrayList ValidateMember(Class c, String name, StringTokenizer st) throws InlineJavaException { + ArrayList ret = new ArrayList() ; + + // Extract member type + String type = st.nextToken() ; + + // Extract the argument + String arg = st.nextToken() ; + + String key = type + " " + c.getName() + "." + name ; + ArrayList fl = new ArrayList() ; + Class param = null ; + + Member cached = (Member)member_cache.get(key) ; + if (cached != null){ + InlineJavaUtils.debug(3, "member was cached") ; + fl.add(fl.size(), cached) ; + } + else { + Field fa[] = c.getFields() ; + for (int i = 0 ; i < fa.length ; i++){ + Field f = fa[(InlineJavaUtils.ReverseMembers() ? (fa.length - 1 - i) : i)] ; + + if (f.getName().equals(name)){ + InlineJavaUtils.debug(3, "found a " + name + " member") ; + + param = f.getType() ; + String t = param.getName() ; + if (type.equals(t)){ + InlineJavaUtils.debug(3, "has matching type " + t) ; + fl.add(fl.size(), f) ; + } + } + } + } + + // Now we got a list of matching members. + // We have to figure out which one we will call. + if (fl.size() == 0){ + throw new InlineJavaException( + "Member " + name + " of type " + type + " for class " + c.getName() + + " not found") ; + } + else { + // Now we need to force the arguments received to match + // the methods signature. + + // If we have more that one, we use the last one, which is the most + // specialized + Field f = (Field)fl.get(fl.size() - 1) ; + member_cache.put(key, f) ; + param = f.getType() ; + + String msg = "For member " + name + " of class " + c.getName() + ": " ; + try { + ret.add(0, f) ; + ret.add(1, ijc.CastArgument(param, arg)) ; + ret.add(2, param) ; + } + catch (InlineJavaCastException e){ + throw new InlineJavaCastException(msg + e.getMessage()) ; + } + catch (InlineJavaException e){ + throw new InlineJavaException(msg + e.getMessage()) ; + } + } + + return ret ; + } + + + /* + This sets the response that will be returned to the Perl + script + */ + void SetResponse(Object o) throws InlineJavaException { + SetResponse(o, null) ; + } + + + void SetResponse(Object o, Class p) throws InlineJavaException { + response = "ok " + SerializeObject(o, p) ; + } + + + String SerializeObject(Object o, Class p) throws InlineJavaException { + Class c = (o == null ? null : o.getClass()) ; + + if ((c != null)&&(p != null)){ + if (ijc.DoesExtend(c, p) < 0){ + throw new InlineJavaException("Can't cast a " + c.getName() + " to a " + p.getName()) ; + } + else{ + c = p ; + } + } + + if (o == null){ + return "undef:" ; + } + else if ((ijc.ClassIsNumeric(c))||(ijc.ClassIsChar(c))||(ijc.ClassIsString(c))){ + if ((ijs.GetNativeDoubles())&&(ijc.ClassIsDouble(c))){ + Double d = (Double)o ; + long l = Double.doubleToLongBits(d.doubleValue()) ; + char ca[] = new char[8] ; + for (int i = 0 ; i < 8 ; i++){ + ca[i] = (char)((l >> (8 * i)) & 0xFF) ; + } + return "double:" + Encode(new String(ca)) ; + } + else { + return "scalar:" + Encode(o.toString()) ; + } + } + else if (ijc.ClassIsBool(c)){ + String b = o.toString() ; + return "scalar:" + Encode((b.equals("true") ? "1" : "0")) ; + } + else { + if (! (o instanceof org.perl.inline.java.InlineJavaPerlObject)){ + // Here we need to register the object in order to send + // it back to the Perl script. + boolean thrown = false ; + String type = "object" ; + if (o instanceof InlineJavaThrown){ + thrown = true ; + o = ((InlineJavaThrown)o).GetThrowable() ; + c = o.getClass() ; + } + else if (ijc.ClassIsArray(c)){ + type = "array" ; + } + else if (ijc.ClassIsHandle(c)){ + type = "handle" ; + } + int id = ijs.PutObject(o) ; + + return "java_" + type + ":" + (thrown ? "1" : "0") + ":" + String.valueOf(id) + + ":" + c.getName() ; + } + else { + return "perl_object:" + ((InlineJavaPerlObject)o).GetId() + + ":" + ((InlineJavaPerlObject)o).GetPkg() ; + } + } + } + + + byte[] DecodeToByteArray(String s){ + return InlineJavaUtils.DecodeBase64(s.toCharArray()) ; + } + + + String Decode(String s) throws InlineJavaException { + try { + if (encoding != null){ + return new String(DecodeToByteArray(s), encoding) ; + } + else { + return new String(DecodeToByteArray(s)) ; + } + } + catch (UnsupportedEncodingException e){ + throw new InlineJavaException("Unsupported encoding: " + e.getMessage()) ; + } + } + + + String EncodeFromByteArray(byte bytes[]){ + return new String(InlineJavaUtils.EncodeBase64(bytes)) ; + } + + + String Encode(String s) throws InlineJavaException { + try { + if (encoding != null){ + return EncodeFromByteArray(s.getBytes(encoding)) ; + } + else { + return EncodeFromByteArray(s.getBytes()) ; + } + } + catch (UnsupportedEncodingException e){ + throw new InlineJavaException("Unsupported encoding: " + e.getMessage()) ; + } + } + + + String GetResponse(){ + return response ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaServer.java b/Java/sources/org/perl/inline/java/InlineJavaServer.java new file mode 100644 index 0000000..1c62ef9 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaServer.java @@ -0,0 +1,337 @@ +package org.perl.inline.java ; + +import java.net.* ; +import java.io.* ; +import java.util.* ; + + +/* + This is the server that will answer all the requests for and on Java + objects. +*/ +public class InlineJavaServer { + private static InlineJavaServer instance = null ; + private String host = null ; + private int port = 0 ; + private boolean shared_jvm = false ; + private boolean priv = false ; + private boolean native_doubles = false ; + + private boolean finished = false ; + private ServerSocket server_socket = null ; + private InlineJavaUserClassLoader ijucl = null ; + private HashMap thread_objects = new HashMap() ; + private int objid = 1 ; + private boolean jni = false ; + private Thread creator = null ; + private int thread_count = 0 ; + + + // This constructor is used in JNI mode + private InlineJavaServer(int debug, boolean _native_doubles){ + init(debug, _native_doubles) ; + + jni = true ; + AddThread(creator) ; + } + + + // This constructor is used in server mode + // Normally one would then call RunMainLoop() + /* Note: Consider http://groups.google.com/group/perl.inline/tree/browse_frm/thread/aa7f5ce236f6d576/3db48a308a8175fb?rnum=1&hl=en&q=Congratulations+with+Inline%3A%3AJava+0.51&_done=%2Fgroup%2Fperl.inline%2Fbrowse_frm%2Fthread%2Faa7f5ce236f6d576%2Fd2de9cf38429c09c%3Flnk%3Dst%26q%3DCongratulations+with+Inline%3A%3AJava+0.51%26rnum%3D1%26hl%3Den%26#doc_3db48a308a8175fb before changing this prototype */ + public InlineJavaServer(int debug, String _host, int _port, boolean _shared_jvm, boolean _priv, boolean _native_doubles){ + init(debug, _native_doubles) ; + + jni = false ; + host = _host ; + port = _port ; + shared_jvm = _shared_jvm ; + priv = _priv ; + + try { + if ((host == null)||(host.equals(""))||(host.equals("ANY"))){ + server_socket = new ServerSocket(port) ; + } + else { + server_socket = new ServerSocket(port, 0, InetAddress.getByName(host)) ; + } + } + catch (IOException e){ + InlineJavaUtils.Fatal("Can't open server socket on port " + String.valueOf(port) + + ": " + e.getMessage()) ; + } + } + + + public void RunMainLoop(){ + while (! finished){ + try { + String name = "IJST-#" + thread_count++ ; + InlineJavaServerThread ijt = new InlineJavaServerThread(name, this, server_socket.accept(), + (priv ? new InlineJavaUserClassLoader() : ijucl)) ; + ijt.start() ; + if (! shared_jvm){ + try { + ijt.join() ; + } + catch (InterruptedException e){ + } + break ; + } + } + catch (IOException e){ + if (! finished){ + System.err.println("Main Loop IO Error: " + e.getMessage()) ; + System.err.flush() ; + } + } + } + } + + + private synchronized void init(int debug, boolean _native_doubles){ + instance = this ; + creator = Thread.currentThread() ; + InlineJavaUtils.set_debug(debug) ; + native_doubles = _native_doubles ; + + ijucl = new InlineJavaUserClassLoader() ; + } + + + static InlineJavaServer GetInstance(){ + if (instance == null){ + InlineJavaUtils.Fatal("No instance of InlineJavaServer has been created!") ; + } + + return instance ; + } + + + InlineJavaUserClassLoader GetUserClassLoader(){ + Thread t = Thread.currentThread() ; + if (t instanceof InlineJavaServerThread){ + return ((InlineJavaServerThread)t).GetUserClassLoader() ; + } + else { + return ijucl ; + } + } + + + String GetType(){ + return (shared_jvm ? "shared" : "private") ; + } + + + boolean GetNativeDoubles(){ + return native_doubles ; + } + + + boolean IsJNI(){ + return jni ; + } + + + /* + Since this function is also called from the JNI XS extension, + it's best if it doesn't throw any exceptions. + */ + String ProcessCommand(String cmd) { + return ProcessCommand(cmd, true) ; + } + + + String ProcessCommand(String cmd, boolean addlf) { + InlineJavaUtils.debug(3, "packet recv is " + cmd) ; + + String resp = null ; + if (cmd != null){ + InlineJavaProtocol ijp = new InlineJavaProtocol(this, cmd) ; + try { + ijp.Do() ; + InlineJavaUtils.debug(3, "packet sent is " + ijp.GetResponse()) ; + resp = ijp.GetResponse() ; + } + catch (InlineJavaException e){ + // Encode the error in default encoding since we don't want any + // Exceptions thrown here... + String err = "error scalar:" + ijp.EncodeFromByteArray(e.getMessage().getBytes()) ; + InlineJavaUtils.debug(3, "packet sent is " + err) ; + resp = err ; + } + } + else{ + if (! shared_jvm){ + // Probably connection dropped... + InlineJavaUtils.debug(1, "lost connection with client in single client mode. Exiting.") ; + System.exit(1) ; + } + else{ + InlineJavaUtils.debug(1, "lost connection with client in shared JVM mode.") ; + return null ; + } + } + + if (addlf){ + resp = resp + "\n" ; + } + + return resp ; + } + + + /* + This method really has no business here, but for historical reasons + it will remain here. + */ + native String jni_callback(String cmd) ; + + + boolean IsThreadPerlContact(Thread t){ + if (((jni)&&(t == creator))|| + ((! jni)&&(t instanceof InlineJavaServerThread))){ + return true ; + } + + return false ; + } + + + synchronized Object GetObject(int id) throws InlineJavaException { + Object o = null ; + HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ; + + if (h == null){ + throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ; + } + else{ + o = h.get(new Integer(id)) ; + if (o == null){ + throw new InlineJavaException("Can't find object " + id + " for thread " +Thread.currentThread().getName()) ; + } + } + + return o ; + } + + + synchronized int PutObject(Object o) throws InlineJavaException { + HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ; + + int id = objid ; + if (h == null){ + throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ; + } + else{ + h.put(new Integer(objid), o) ; + objid++ ; + } + + return id ; + } + + + synchronized Object DeleteObject(int id) throws InlineJavaException { + Object o = null ; + HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ; + + if (h == null){ + throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ; + } + else{ + o = h.remove(new Integer(id)) ; + if (o == null){ + throw new InlineJavaException("Can't find object " + id + " for thread " + Thread.currentThread().getName()) ; + } + } + + return o ; + } + + + synchronized int ObjectCount() throws InlineJavaException { + int i = -1 ; + HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ; + + if (h == null){ + throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ; + } + else{ + i = h.values().size() ; + } + + return i ; + } + + + public synchronized void StopMainLoop(){ + if (! jni){ + try { + finished = true ; + server_socket.close() ; + } + catch (IOException e){ + System.err.println("Shutdown IO Error: " + e.getMessage()) ; + System.err.flush() ; + } + } + } + + + synchronized void Shutdown(){ + StopMainLoop() ; + System.exit(0) ; + } + + + /* + Here the prototype accepts Threads because the JNI thread + calls this method also. + */ + synchronized void AddThread(Thread t){ + thread_objects.put(t, new HashMap()) ; + InlineJavaPerlCaller.AddThread(t) ; + } + + + synchronized void RemoveThread(InlineJavaServerThread t){ + thread_objects.remove(t) ; + InlineJavaPerlCaller.RemoveThread(t) ; + } + + + + /* + Startup + */ + public static void main(String[] argv){ + int debug = Integer.parseInt(argv[0]) ; + String host = argv[1] ; + int port = Integer.parseInt(argv[2]) ; + boolean shared_jvm = new Boolean(argv[3]).booleanValue() ; + boolean priv = new Boolean(argv[4]).booleanValue() ; + boolean native_doubles = new Boolean(argv[5]).booleanValue() ; + + InlineJavaServer ijs = new InlineJavaServer(debug, host, port, shared_jvm, priv, native_doubles) ; + ijs.RunMainLoop() ; + System.exit(0) ; + } + + + /* + With PerlInterpreter this is called twice, but we don't want to create + a new object the second time. + */ + public static InlineJavaServer jni_main(int debug, boolean native_doubles){ + if (instance != null){ + InlineJavaUtils.set_debug(debug) ; + InlineJavaUtils.debug(1, "recycling InlineJavaServer created by PerlInterpreter") ; + return instance ; + } + else { + return new InlineJavaServer(debug, native_doubles) ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaServerThread.java b/Java/sources/org/perl/inline/java/InlineJavaServerThread.java new file mode 100644 index 0000000..e39e4e9 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaServerThread.java @@ -0,0 +1,69 @@ +package org.perl.inline.java ; + +import java.io.* ; +import java.net.* ; +import java.util.* ; + + +class InlineJavaServerThread extends Thread { + private InlineJavaServer ijs ; + private Socket client ; + private BufferedReader br ; + private BufferedWriter bw ; + private InlineJavaUserClassLoader ijucl ; + + + InlineJavaServerThread(String name, InlineJavaServer _ijs, Socket _client, InlineJavaUserClassLoader _ijucl) throws IOException { + super(name) ; + client = _client ; + ijs = _ijs ; + ijucl = _ijucl ; + + InputStreamReader ir = new InputStreamReader(client.getInputStream()) ; + OutputStreamWriter or = new OutputStreamWriter(client.getOutputStream()) ; + br = new BufferedReader(ir) ; + bw = new BufferedWriter(or) ; + } + + + BufferedReader GetReader(){ + return br ; + } + + + BufferedWriter GetWriter(){ + return bw ; + } + + + InlineJavaUserClassLoader GetUserClassLoader(){ + return ijucl ; + } + + + public void run(){ + try { + ijs.AddThread(this) ; + + while (true){ + String cmd = br.readLine() ; + + String resp = ijs.ProcessCommand(cmd) ; + if (resp != null){ + bw.write(resp) ; + bw.flush() ; + } + else { + client.close() ; + break ; + } + } + } + catch (IOException e){ + System.err.println("IO error: " + e.getMessage()) ; + } + finally { + ijs.RemoveThread(this) ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaThrown.java b/Java/sources/org/perl/inline/java/InlineJavaThrown.java new file mode 100644 index 0000000..38bee49 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaThrown.java @@ -0,0 +1,14 @@ +package org.perl.inline.java ; + + +class InlineJavaThrown { + Throwable t ; + + InlineJavaThrown(Throwable _t){ + t = _t ; + } + + Throwable GetThrowable(){ + return t ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java b/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java new file mode 100644 index 0000000..0f2d97a --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java @@ -0,0 +1,45 @@ +import java.util.* ; +import java.lang.reflect.* ; + + +public class InlineJavaUserClassLink { + public InlineJavaUserClassLink(){ + } + + + public Object invoke(Method m, Object o, Object p[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + return m.invoke(o, p) ; + } + + + public Object get(Field f, Object o) throws IllegalAccessException, IllegalArgumentException { + return f.get(o) ; + } + + + public void set(Field f, Object o, Object p) throws IllegalAccessException, IllegalArgumentException { + f.set(o, p) ; + } + + + public Object array_get(Object o, Integer idx){ + return Array.get(o, idx.intValue()) ; + } + + + public void array_set(Object o, Integer idx, Object elem) throws IllegalArgumentException { + Array.set(o, idx.intValue(), elem) ; + } + + + public Object create(Class p, Object args[], Class proto[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + // This will allow usage of the default no-arg constructor + if (proto.length == 0){ + return p.newInstance() ; + } + else{ + Constructor con = (Constructor)p.getConstructor(proto) ; + return con.newInstance(args) ; + } + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java b/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java new file mode 100644 index 0000000..0105872 --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java @@ -0,0 +1,192 @@ +package org.perl.inline.java ; + +import java.net.* ; +import java.util.* ; +import java.io.* ; +import java.lang.reflect.* ; + + +/* + This is the ClassLoader that loads the users code. It is also + used to pass reflection calls to the InlineJavaUserClassLink + so that it will execute them. +*/ +class InlineJavaUserClassLoader extends URLClassLoader { + private HashMap urls = new HashMap() ; + + private Object link = null ; + private Method invoke = null ; + private Method get = null ; + private Method set = null ; + private Method array_get = null ; + private Method array_set = null ; + private Method create = null ; + + + public InlineJavaUserClassLoader(){ + // Added Thread.currentThread().getContextClassLoader() so that the code works + // in Tomcat and possibly other embedded environments asa well. + super(new URL [] {}, Thread.currentThread().getContextClassLoader()) ; + } + + + public void AddClassPath(String path) throws InlineJavaException { + try { + File p = new File(path) ; + URL u = p.toURI().toURL() ; + if (urls.get(u) == null){ + urls.put(u, "1") ; + addURL(u) ; + InlineJavaUtils.debug(2, "added " + u + " to classpath") ; + } + } + catch (MalformedURLException e){ + throw new InlineJavaException("Can't add invalid classpath entry '" + path + "'") ; + } + } + + + synchronized private void check_link() throws InlineJavaException { + if (link == null){ + try { + InlineJavaUtils.debug(1, "loading InlineJavaUserClassLink via InlineJavaUserClassLoader") ; + Class c = Class.forName("InlineJavaUserClassLink", true, this) ; + link = c.newInstance() ; + + invoke = find_method(c, "invoke") ; + get = find_method(c, "get") ; + set = find_method(c, "set") ; + array_get = find_method(c, "array_get") ; + array_set = find_method(c, "array_set") ; + create = find_method(c, "create") ; + } + catch (Exception e){ + throw new InlineJavaException("InlineJavaUserClassLoader can't load InlineJavaUserClassLink: invalid classpath setup (" + + e.getClass().getName() + ": " + e.getMessage() + ")") ; + } + } + } + + + private Method find_method(Class c, String name) throws InlineJavaException { + Method ml[] = c.getMethods() ; + for (int i = 0 ; i < ml.length ; i++){ + if (ml[i].getName().equals(name)){ + return ml[i] ; + } + } + + throw new InlineJavaException("Can't find method '" + name + + "' in class InlineJavaUserClassLink") ; + } + + + private Object invoke_via_link(Method m, Object p[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException { + try { + return m.invoke(link, p) ; + } + catch (IllegalAccessException e){ + throw new InlineJavaException("Can't invoke method from class InlineJavaUserClassLink: IllegalAccessException") ; + } + catch (IllegalArgumentException e){ + throw new InlineJavaException("Can't invoke method from class InlineJavaUserClassLink: IllegalArgumentException") ; + } + catch (InvocationTargetException e){ + Throwable t = e.getTargetException() ; + if (t instanceof NoSuchMethodException){ + throw (NoSuchMethodException)t ; + } + else if (t instanceof InstantiationException){ + throw (InstantiationException)t ; + } + else if (t instanceof IllegalAccessException){ + throw (IllegalAccessException)t ; + } + if (t instanceof IllegalAccessException){ + throw (IllegalAccessException)t ; + } + else if (t instanceof IllegalArgumentException){ + throw (IllegalArgumentException)t ; + } + else if (t instanceof InvocationTargetException){ + throw (InvocationTargetException)t ; + } + // Not sure if this is really necessary, but... + else if (t instanceof RuntimeException){ + RuntimeException re = (RuntimeException)t ; + throw re ; + } + else{ + // In theory this case is impossible. + throw new InlineJavaException("Unexpected exception of type '" + + t.getClass().getName() + "': " + t.getMessage()) ; + } + } + } + + + public Object invoke(Method m, Object o, Object p[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException { + check_link() ; + try { + return invoke_via_link(invoke, new Object [] {m, o, p}) ; + } + catch (NoSuchMethodException me){/* Impossible */} + catch (InstantiationException ie){/* Impossible */} + return null ; + } + + + public Object get(Field f, Object o) throws IllegalAccessException, IllegalArgumentException, InlineJavaException { + check_link() ; + try { + return invoke_via_link(get, new Object [] {f, o}) ; + } + catch (NoSuchMethodException me){/* Impossible */} + catch (InstantiationException ie){/* Impossible */} + catch (InvocationTargetException e){/* Impossible */} + return null ; + } + + + public void set(Field f, Object o, Object p) throws IllegalAccessException, IllegalArgumentException, InlineJavaException { + check_link() ; + try { + invoke_via_link(set, new Object [] {f, o, p}) ; + } + catch (NoSuchMethodException me){/* Impossible */} + catch (InstantiationException ie){/* Impossible */} + catch (InvocationTargetException e){/* Impossible */} + } + + + public Object array_get(Object o, int idx) throws InlineJavaException { + check_link() ; + try { + return invoke_via_link(array_get, new Object [] {o, new Integer(idx)}) ; + } + catch (NoSuchMethodException me){/* Impossible */} + catch (InstantiationException ie){/* Impossible */} + catch (IllegalAccessException iae){/* Impossible */} + catch (IllegalArgumentException iae){/* Impossible */} + catch (InvocationTargetException e){/* Impossible */} + return null ; + } + + + public void array_set(Object o, int idx, Object elem) throws IllegalArgumentException, InlineJavaException { + check_link() ; + try { + invoke_via_link(array_set, new Object [] {o, new Integer(idx), elem}) ; + } + catch (NoSuchMethodException me){/* Impossible */} + catch (InstantiationException ie){/* Impossible */} + catch (IllegalAccessException iae){/* Impossible */} + catch (InvocationTargetException e){/* Impossible */} + } + + + public Object create(Class p, Object args[], Class proto[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException { + check_link() ; + return invoke_via_link(create, new Object [] {p, args, proto}) ; + } +} diff --git a/Java/sources/org/perl/inline/java/InlineJavaUtils.java b/Java/sources/org/perl/inline/java/InlineJavaUtils.java new file mode 100644 index 0000000..65426dd --- /dev/null +++ b/Java/sources/org/perl/inline/java/InlineJavaUtils.java @@ -0,0 +1,175 @@ +package org.perl.inline.java ; + +import java.util.* ; + + +/* + Creates a string representing a method signature +*/ +class InlineJavaUtils { + private static int debug = 0 ; + + + public synchronized static void set_debug(int d){ + debug = d ; + } + + + public static int get_debug(){ + return debug ; + } + + + static String CreateSignature(Class param[]){ + return CreateSignature(param, ", ") ; + } + + + static String CreateSignature(Class param[], String del){ + StringBuffer ret = new StringBuffer() ; + for (int i = 0 ; i < param.length ; i++){ + if (i > 0){ + ret.append(del) ; + } + ret.append(param[i].getName()) ; + } + + return "(" + ret.toString() + ")" ; + } + + + synchronized static void debug(int level, String s) { + if ((debug > 0)&&(debug >= level)){ + StringBuffer sb = new StringBuffer() ; + for (int i = 0 ; i < level ; i++){ + sb.append(" ") ; + } + System.err.println("[java][" + level + "]" + sb.toString() + s) ; + System.err.flush() ; + } + } + + + static void Fatal(String msg){ + System.err.println(msg) ; + System.err.flush() ; + System.exit(1) ; + } + + + static boolean ReverseMembers() { + String v = System.getProperty("java.version") ; + boolean no_rev = ((v.startsWith("1.2"))||(v.startsWith("1.3"))) ; + + return (! no_rev) ; + } + + + + /* + Base64 stuff. This section conatins code by Christian d'Heureuse that is + licended under the LGPL. Used by permission: + + From: Christian d'Heureuse <chdh@inventec.ch> + To: Patrick LeBoutillier <patrick.leboutillier@gmail.com> + Date: Aug 11, 2005 4:45 AM + Subject: Re: Base64Coder + + > I was wondering if you can grant me permission to include your + > code in my project. + + Yes, I grant you permission to include the Base64Coder class in your + project. + + * + * A Base64 Encoder/Decoder. + * + * This class is used to encode and decode data in Base64 format + * as described in RFC 1521. + * + * <p> + * Copyright 2003: Christian d'Heureuse, Inventec Informatik AG, Switzerland.<br> + * License: This is "Open Source" software and released under the <a href="http://www.gnu.org/licenses/lgpl.html" target="_top">GNU/LGPL</a> license. + * It is provided "as is" without warranty of any kind. Please contact the author for other licensing arrangements.<br> + * Home page: <a href="http://www.source-code.biz" target="_top">www.source-code.biz</a><br> + * + * <p> + * Version history:<br> + * 2003-07-22 Christian d'Heureuse (chdh): Module created.<br> + * 2005-08-11 chdh: Lincense changed from GPL to LGPL. + * + */ + + // Mapping table from 6-bit nibbles to Base64 characters. + private static char[] map1 = new char[64]; + static { + int i=0; + for (char c='A'; c<='Z'; c++) map1[i++] = c; + for (char c='a'; c<='z'; c++) map1[i++] = c; + for (char c='0'; c<='9'; c++) map1[i++] = c; + map1[i++] = '+'; map1[i++] = '/'; + } + + // Mapping table from Base64 characters to 6-bit nibbles. + private static byte[] map2 = new byte[128]; + static { + for (int i=0; i<map2.length; i++) map2[i] = -1; + for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; + } + + + public static char[] EncodeBase64(byte[] in){ + int iLen = in.length; + int oDataLen = (iLen*4+2)/3; // output length without padding + int oLen = ((iLen+2)/3)*4; // output length including padding + char[] out = new char[oLen]; + int ip = 0; + int op = 0; + while (ip < iLen) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iLen ? in[ip++] & 0xff : 0; + int i2 = ip < iLen ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = map1[o0]; + out[op++] = map1[o1]; + out[op] = op < oDataLen ? map1[o2] : '='; op++; + out[op] = op < oDataLen ? map1[o3] : '='; op++; + } + return out; + } + + + public static byte[] DecodeBase64(char[] in){ + int iLen = in.length; + if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); + while (iLen > 0 && in[iLen-1] == '=') iLen--; + int oLen = (iLen*3) / 4; + byte[] out = new byte[oLen]; + int ip = 0; + int op = 0; + while (ip < iLen) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iLen ? in[ip++] : 'A'; + int i3 = ip < iLen ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) + throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) + throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); + int o0 = ( b0 <<2) | (b1>>>4); + int o1 = ((b1 & 0xf)<<4) | (b2>>>2); + int o2 = ((b2 & 3)<<6) | b3; + out[op++] = (byte)o0; + if (op<oLen) out[op++] = (byte)o1; + if (op<oLen) out[op++] = (byte)o2; + } + return out; + } +} |