Hi, Another one: - change style to Java style (braces, spacing general layout) - remove extra lines
For this one the code makes use of a tempdir specified as "." - wouldn't it make sense to use java.io.tmpdir? If the generated files were created in a subdirectory of the real tmpdir, then it would be possible to simply rmdir after instead of using the much more complicated Filter code. Kev
Index: . =================================================================== --- . (revision 438751) +++ . (working copy) @@ -81,291 +81,272 @@ * <p> * @author Joe Kesselman */ -public class JavaEngine extends BSFEngineImpl -{ - Class javaclass=null; - static Hashtable codeToClass=new Hashtable(); - static String serializeCompilation=""; - static String placeholder="$$CLASSNAME$$"; - String minorPrefix; - - private Log logger = LogFactory.getLog(this.getClass().getName()); - - /** - * Create a scratchfile, open it for writing, return its name. - * Relies on the filesystem to provide us with uniqueness testing. - * NOTE THAT uniqueFileOffset continues to count; we don't want to - * risk reusing a classname we have previously loaded in this session - * even if the classfile has been deleted. - */ - private int uniqueFileOffset=-1; - private class GeneratedFile - { - File file=null; - FileOutputStream fos=null; - String className=null; - GeneratedFile(File file,FileOutputStream fos,String className) - { - this.file=file; - this.fos=fos; - this.className=className; - } - } - /** - * Constructor. - */ - public JavaEngine () - { - // Do compilation-possible check here?????????????? - } - public Object call (Object object, String method, Object[] args) - throws BSFException - { - throw new BSFException (BSFException.REASON_UNSUPPORTED_FEATURE, - "call() is not currently supported by JavaEngine"); - } - public void compileScript (String source, int lineNo, int columnNo, - Object script, CodeBuffer cb) throws BSFException { - ObjInfo oldRet = cb.getFinalServiceMethodStatement (); - - if (oldRet != null && oldRet.isExecutable ()) { - cb.addServiceMethodStatement (oldRet.objName + ";"); - } - - cb.addServiceMethodStatement (script.toString ()); - cb.setFinalServiceMethodStatement (null); - } - /** - * This is used by an application to evaluate a string containing - * some expression. It should store the "bsf" handle where the - * script can get to it, for callback purposes. - * <p> - * Note that Java compilation imposes serious overhead, - * but in exchange you get full Java performance - * once the classes have been created (minus the cache lookup cost). - * <p> - * Nobody knows whether javac is threadsafe. - * I'm going to serialize access to protect it. - * <p> - * There is no published API for invoking javac as a class. There's a trick - * that seems to work for Java 1.1.x, but it stopped working in Java 1.2. - * We will attempt to use it, then if necessary fall back on invoking - * javac via the command line. - */ - public Object eval (String source, int lineNo, int columnNo, - Object oscript) throws BSFException - { - Object retval=null; - String classname=null; - GeneratedFile gf=null; - - String basescript=oscript.toString(); - String script=basescript; // May be altered by $$CLASSNAME$$ expansion - - try { - // Do we already have a class exactly matching this code? - javaclass=(Class)codeToClass.get(basescript); - - if(javaclass!=null) - { - classname=javaclass.getName(); - } - else - { - gf=openUniqueFile(tempDir, "BSFJava",".java"); - if(gf==null) - throw new BSFException("couldn't create JavaEngine scratchfile"); - - // Obtain classname - classname=gf.className; - - // Write the kluge header to the file. - gf.fos.write(("import java.lang.*;"+ - "import java.util.*;"+ - "public class "+classname+" {\n" + - " static public Object BSFJavaEngineEntry(org.apache.bsf.BSFManager bsf) {\n") - .getBytes()); - - // Edit the script to replace placeholder with the generated - // classname. Note that this occurs _after_ the cache was checked! - int startpoint,endpoint; - if((startpoint=script.indexOf(placeholder))>=0) - { - StringBuffer changed=new StringBuffer(); - for(; - startpoint>=0; - startpoint=script.indexOf(placeholder,startpoint)) - { - changed.setLength(0); // Reset for 2nd pass or later - if(startpoint>0) - changed.append(script.substring(0,startpoint)); - changed.append(classname); - endpoint=startpoint+placeholder.length(); - if(endpoint<script.length()) - changed.append(script.substring(endpoint)); - script=changed.toString(); - } - } - - // MJD - debug -// BSFDeclaredBean tempBean; -// String className; -// -// for (int i = 0; i < declaredBeans.size (); i++) { -// tempBean = (BSFDeclaredBean) declaredBeans.elementAt (i); -// className = StringUtils.getClassName (tempBean.bean.getClass ()); -// -// gf.fos.write ((className + " " + -// tempBean.name + " = (" + className + -// ")bsf.lookupBean(\"" + -// tempBean.name + "\");").getBytes ()); -// } - // MJD - debug - - // Copy the input to the file. - // Assumes all available -- probably mistake, but same as other engines. - gf.fos.write(script.getBytes()); - // Close the method and class - gf.fos.write(("\n }\n}\n").getBytes()); - gf.fos.close(); - - // Compile through Java to .class file - // May not be threadsafe. Serialize access on static object: - synchronized(serializeCompilation) - { - JavaUtils.JDKcompile(gf.file.getPath(), classPath); - } - - // Load class. - javaclass=EngineUtils.loadClass (mgr, classname); - - // Stash class for reuse - codeToClass.put(basescript,javaclass); - } - - Object[] callArgs={mgr}; - retval=internal_call(this,"BSFJavaEngineEntry",callArgs); - } - - - catch(Exception e) - { - e.printStackTrace (); - throw new BSFException (BSFException.REASON_IO_ERROR, e.getMessage ()); - } - finally - { - // Cleanup: delete the .java and .class files - -// if(gf!=null && gf.file!=null && gf.file.exists()) -// gf.file.delete(); // .java file - - - if(classname!=null) - { - // Generated class - File file=new File(tempDir+File.separatorChar+classname+".class"); -// if(file.exists()) -// file.delete(); - - // Search for and clean up minor classes, classname$xxx.class - file=new File(tempDir); // ***** Is this required? - minorPrefix=classname+"$"; // Indirect arg to filter - String[] minor_classfiles= - file.list(new FilenameFilter() - { - // Starts with classname$ and ends with .class - public boolean accept(File dir,String name) - { - return - (0==name.indexOf(minorPrefix)) - && - (name.lastIndexOf(".class")==name.length()-6) - ; - } - }); - for(int i=0;i<minor_classfiles.length;++i) - { - file=new File(minor_classfiles[i]); -// file.delete(); - } - } - } - - return retval; - } - public void initialize (BSFManager mgr, String lang, - Vector declaredBeans) throws BSFException { - super.initialize (mgr, lang, declaredBeans); - } - /** - * Return an object from an extension. - * @param object Object on which to make the internal_call (ignored). - * @param method The name of the method to internal_call. - * @param args an array of arguments to be - * passed to the extension, which may be either - * Vectors of Nodes, or Strings. - */ - Object internal_call (Object object, String method, Object[] args) - throws BSFException - { - //***** ISSUE: Only static methods are currently supported - Object retval=null; - try - { - if(javaclass!=null) - { - //***** This should call the lookup used in BML, for typesafety - Class[] argtypes=new Class[args.length]; - for(int i=0;i<args.length;++i) - argtypes[i]=args[i].getClass(); - - Method m=MethodUtils.getMethod(javaclass,method,argtypes); - retval=m.invoke(null,args); - } - } - catch(Exception e) - { - throw new BSFException (BSFException.REASON_IO_ERROR, e.getMessage ()); - } - return retval; - } - private GeneratedFile openUniqueFile(String directory,String prefix,String suffix) - { - File file=null; - FileOutputStream fos=null; - int max=1000; // Don't try forever - GeneratedFile gf=null; - int i; - String className = null; - for(i=max,++uniqueFileOffset; - fos==null && i>0; - --i,++uniqueFileOffset) - { - // Probably a timing hazard here... *************** - try - { - className = prefix+uniqueFileOffset; - file=new File(directory+File.separatorChar+className+suffix); - if(file!=null && !file.exists()) - fos=new FileOutputStream(file); - } - catch(Exception e) - { - // File could not be opened for write, or Security Exception - // was thrown. If someone else created the file before we could - // open it, that's probably a threading conflict and we don't - // bother reporting it. - if(!file.exists()) - { - logger.error("openUniqueFile: unexpected ", e); - } - } - } - if(fos==null) - logger.error("openUniqueFile: Failed "+max+"attempts."); - else - gf=new GeneratedFile(file,fos,className); - return gf; - } +public class JavaEngine extends BSFEngineImpl { + Class javaclass = null; + static Hashtable codeToClass = new Hashtable(); + static String serializeCompilation = ""; + static String placeholder = "$$CLASSNAME$$"; + String minorPrefix; + + private Log logger = LogFactory.getLog(this.getClass().getName()); + + /** + * Create a scratchfile, open it for writing, return its name. + * Relies on the filesystem to provide us with uniqueness testing. + * NOTE THAT uniqueFileOffset continues to count; we don't want to + * risk reusing a classname we have previously loaded in this session + * even if the classfile has been deleted. + */ + private int uniqueFileOffset = -1; + + private class GeneratedFile { + File file = null; + FileOutputStream fos = null; + String className = null; + GeneratedFile(File file, FileOutputStream fos, String className) { + this.file = file; + this.fos = fos; + this.className = className; + } + } + + /** + * Constructor. + */ + public JavaEngine () { + // Do compilation-possible check here?????????????? + } + + public Object call (Object object, String method, Object[] args) + throws BSFException + { + throw new BSFException (BSFException.REASON_UNSUPPORTED_FEATURE, + "call() is not currently supported by JavaEngine"); + } + + public void compileScript (String source, int lineNo, int columnNo, + Object script, CodeBuffer cb) throws BSFException { + ObjInfo oldRet = cb.getFinalServiceMethodStatement (); + + if (oldRet != null && oldRet.isExecutable ()) { + cb.addServiceMethodStatement (oldRet.objName + ";"); + } + + cb.addServiceMethodStatement (script.toString ()); + cb.setFinalServiceMethodStatement (null); + } + + /** + * This is used by an application to evaluate a string containing + * some expression. It should store the "bsf" handle where the + * script can get to it, for callback purposes. + * <p> + * Note that Java compilation imposes serious overhead, + * but in exchange you get full Java performance + * once the classes have been created (minus the cache lookup cost). + * <p> + * Nobody knows whether javac is threadsafe. + * I'm going to serialize access to protect it. + * <p> + * There is no published API for invoking javac as a class. There's a trick + * that seems to work for Java 1.1.x, but it stopped working in Java 1.2. + * We will attempt to use it, then if necessary fall back on invoking + * javac via the command line. + */ + public Object eval (String source, int lineNo, int columnNo, + Object oscript) throws BSFException + { + Object retval = null; + String classname = null; + GeneratedFile gf = null; + + String basescript = oscript.toString(); + String script = basescript; // May be altered by $$CLASSNAME$$ expansion + + try { + // Do we already have a class exactly matching this code? + javaclass = (Class)codeToClass.get(basescript); + + if(javaclass != null) { + classname=javaclass.getName(); + } else { + gf = openUniqueFile(tempDir, "BSFJava",".java"); + if( gf == null) { + throw new BSFException("couldn't create JavaEngine scratchfile"); + } + // Obtain classname + classname = gf.className; + + // Write the kluge header to the file. + gf.fos.write(("import java.lang.*;"+ + "import java.util.*;"+ + "public class "+classname+" {\n" + + " static public Object BSFJavaEngineEntry(org.apache.bsf.BSFManager bsf) {\n") + .getBytes()); + + // Edit the script to replace placeholder with the generated + // classname. Note that this occurs _after_ the cache was checked! + int startpoint = script.indexOf(placeholder); + int endpoint; + if(startpoint >= 0) { + StringBuffer changed = new StringBuffer(); + for(; startpoint >=0; startpoint = script.indexOf(placeholder,startpoint)) { + changed.setLength(0); // Reset for 2nd pass or later + if(startpoint > 0) { + changed.append(script.substring(0,startpoint)); + } + changed.append(classname); + endpoint = startpoint+placeholder.length(); + if(endpoint < script.length()) { + changed.append(script.substring(endpoint)); + } + script = changed.toString(); + } + } + + // MJD - debug +// BSFDeclaredBean tempBean; +// String className; +// +// for (int i = 0; i < declaredBeans.size (); i++) { +// tempBean = (BSFDeclaredBean) declaredBeans.elementAt (i); +// className = StringUtils.getClassName (tempBean.bean.getClass ()); +// +// gf.fos.write ((className + " " + +// tempBean.name + " = (" + className + +// ")bsf.lookupBean(\"" + +// tempBean.name + "\");").getBytes ()); +// } + // MJD - debug + + // Copy the input to the file. + // Assumes all available -- probably mistake, but same as other engines. + gf.fos.write(script.getBytes()); + // Close the method and class + gf.fos.write(("\n }\n}\n").getBytes()); + gf.fos.close(); + + // Compile through Java to .class file + // May not be threadsafe. Serialize access on static object: + synchronized(serializeCompilation) { + JavaUtils.JDKcompile(gf.file.getPath(), classPath); + } + + // Load class. + javaclass = EngineUtils.loadClass(mgr, classname); + + // Stash class for reuse + codeToClass.put(basescript, javaclass); + } + + Object[] callArgs = {mgr}; + retval = internalCall(this,"BSFJavaEngineEntry",callArgs); + } + + + catch(Exception e) { + e.printStackTrace (); + throw new BSFException (BSFException.REASON_IO_ERROR, e.getMessage ()); + } finally { + // Cleanup: delete the .java and .class files + +// if(gf!=null && gf.file!=null && gf.file.exists()) +// gf.file.delete(); // .java file + + + if(classname!=null) { + // Generated class + File file = new File(tempDir+File.separatorChar+classname+".class"); +// if(file.exists()) +// file.delete(); + + // Search for and clean up minor classes, classname$xxx.class + file = new File(tempDir); // ***** Is this required? + minorPrefix = classname+"$"; // Indirect arg to filter + String[] minorClassfiles = file.list(new FilenameFilter() + { + // Starts with classname$ and ends with .class + public boolean accept(File dir,String name) { + return + (0 == name.indexOf(minorPrefix)) + && + (name.lastIndexOf(".class") == name.length()-6); + } + }); + for(int i = 0; i < minorClassfiles.length; ++i) { + file = new File(minorClassfiles[i]); +// file.delete(); + } + } + } + return retval; + } + + public void initialize (BSFManager mgr, String lang, + Vector declaredBeans) throws BSFException { + super.initialize (mgr, lang, declaredBeans); + } + /** + * Return an object from an extension. + * @param object Object on which to make the internal_call (ignored). + * @param method The name of the method to internal_call. + * @param args an array of arguments to be + * passed to the extension, which may be either + * Vectors of Nodes, or Strings. + */ + Object internalCall (Object object, String method, Object[] args) + throws BSFException + { + //***** ISSUE: Only static methods are currently supported + Object retval = null; + try { + if(javaclass != null) { + //***** This should call the lookup used in BML, for typesafety + Class[] argtypes = new Class[args.length]; + for(int i=0; i<args.length; ++i) { + argtypes[i]=args[i].getClass(); + } + Method m = MethodUtils.getMethod(javaclass, method, argtypes); + retval = m.invoke(null, args); + } + } + catch(Exception e) { + throw new BSFException (BSFException.REASON_IO_ERROR, e.getMessage ()); + } + return retval; + } + + private GeneratedFile openUniqueFile(String directory,String prefix,String suffix) { + File file = null; + FileOutputStream fos = null; + int max = 1000; // Don't try forever + GeneratedFile gf = null; + int i; + String className = null; + for(i=max,++uniqueFileOffset; fos==null && i>0;--i,++uniqueFileOffset) { + // Probably a timing hazard here... *************** + try { + className = prefix+uniqueFileOffset; + file = new File(directory+File.separatorChar+className+suffix); + if(file != null && !file.exists()) { + fos = new FileOutputStream(file); + } + } + catch(Exception e) { + // File could not be opened for write, or Security Exception + // was thrown. If someone else created the file before we could + // open it, that's probably a threading conflict and we don't + // bother reporting it. + if(!file.exists()) { + logger.error("openUniqueFile: unexpected ", e); + } + } + } + if(fos==null) { + logger.error("openUniqueFile: Failed "+max+"attempts."); + } else { + gf = new GeneratedFile(file,fos,className); + } + return gf; + } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]