/* * Copyright (C) 2000 - 2010 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package org.alanwilliamson.lang.java; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import org.aw20.security.MD5; import com.nary.io.StreamUtils; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.parser.script.JavaBlock; public class JavaClassFactory { public int SRC_OFFSET_INLINE = 16; public String javaBlockTemplate, javaClassTemplate; private static String PACKAGE = "cfscript.java"; private ClassLoader classLoader; private Hashtable<String,Object> attributes = null; public JavaClassFactory() throws IOException{ classLoader = cfSCRIPTJava.class.getClassLoader(); javaBlockTemplate = StreamUtils.readToString( this.getClass().getResourceAsStream("javablock.java.txt") ); javaClassTemplate = StreamUtils.readToString( this.getClass().getResourceAsStream("javaclass.java.txt") ); attributes = new Hashtable<String,Object>(); } public Hashtable<String,Object> getAttributes(){ return attributes; } public JavaBlock getCompiledClass( String javaTemplate, String javaSnippet, String jarlist, String importlist ) throws Exception { String CLASSNAME = "cf" + MD5.getDigest(javaSnippet); importlist = getImportList( importlist ); JavaImport j = extractImportList(javaSnippet); javaSnippet = j.code; importlist += ";" + j.importlist; String javaCode = com.nary.util.string.replaceString(javaTemplate, "%PACKAGE%", PACKAGE, false ); javaCode = com.nary.util.string.replaceString(javaCode, "%IMPORT%", importlist, false ); javaCode = com.nary.util.string.replaceString(javaCode, "%CLASSNAME%", CLASSNAME, false ); javaCode = com.nary.util.string.replaceString(javaCode, "%JAVASNIPPET%", javaSnippet, false ); CharSequenceCompiler<JavaBlock> compiler = new CharSequenceCompiler<JavaBlock>( classLoader, getClasspath(jarlist) ); try { Class<JavaBlock> newClass = compiler.compile( PACKAGE + "." + CLASSNAME, javaCode, null ); return newClass.newInstance(); } catch (IllegalAccessException e) { cfEngine.log( e.getMessage() ); } catch (InstantiationException e) { cfEngine.log( e.getMessage() ); } return null; } /** * Retrieves all the imports for the generated class * * @param javaSnippet * @param importlist * @return */ class JavaImport { public String code, importlist; } private JavaImport extractImportList(String javaSnippet) { StringBuilder sb = new StringBuilder(32); // Attempt to extract them from the body int c1=0, c2; while ( c1 != -1 ){ c1 = javaSnippet.indexOf("import ", c1+1); if ( c1 != -1 ){ c2 = javaSnippet.indexOf(";", c1+1); if ( c2 != -1 ){ sb.append( javaSnippet.substring(c1, c2+1) ); javaSnippet = javaSnippet.substring(0,c1) + javaSnippet.substring(c2+1); } } } JavaImport j = new JavaImport(); j.code = javaSnippet; j.importlist = sb.toString(); return j; } private String getImportList(String importlist) { StringBuilder sb = new StringBuilder(32); if ( importlist != null && importlist.length() > 0 ){ String[] packages = importlist.split(","); for ( int x=0; x < packages.length; x++ ){ if ( packages[x].trim().length() == 0 ) continue; if ( packages[x].startsWith("import ") ){ packages[x] = packages[x].substring( packages[x].indexOf(" ") ); } if ( packages[x].endsWith(";")) packages[x] = packages[x].substring(0,packages[x].length()-1); sb.append( "import " + packages[x].trim() + ";" ); } } return sb.toString(); } private List<String> getClasspath(String jarlist) throws Exception{ StringBuilder sb = new StringBuilder(1024); String jar = findJarPath("classes/"); if (jar != null ){ sb.append( jar + File.pathSeparator ); } // We need the main OpenBlueDragon.jar file String opbdjar = findJarPath("OpenBlueDragon.jar"); if (opbdjar == null ){ if ( jar != null ){ // final check to see if we are running unexploded jar = jar + "org/alanwilliamson/lang/java/JavaClassFactory.class"; if ( !new File(jar).exists() ) throw new Exception("failed to find: OpenBlueDragon.jar in classpath" ); }else throw new Exception("failed to find: OpenBlueDragon.jar in classpath" ); } sb.append( opbdjar + File.pathSeparator ); if ( jarlist != null ){ String[] list = jarlist.split(","); for ( int x=0; x < list.length; x++ ){ jar = list[x].trim(); jar = findJarPath(jar); if (jar == null ){ throw new Exception("failed to find: " + jar + " in classpath" ); } sb.append( jar + File.pathSeparator ); } } return Arrays.asList(new String[] { "-classpath", sb.toString() }); } /** * Attempts to do a file search for the file path. Returns null if not found * * @param jarfile * @return */ private String findJarPath( String jarfile ){ jarfile = jarfile.replace( '\\', '/' ); ClassLoader cl = ClassLoader.getSystemClassLoader(); String path = findJarPath( ((URLClassLoader)cl).getURLs(), jarfile ); if ( path != null ) return path; cl = getClass().getClassLoader(); path = findJarPath( ((URLClassLoader)cl).getURLs(), jarfile ); if ( path != null ) return path; cl = Thread.currentThread().getContextClassLoader(); path = findJarPath( ((URLClassLoader)cl).getURLs(), jarfile ); if ( path != null ) return path; // could not be found return null; } private String findJarPath( URL[] urls, String jarfile ){ for ( int x=0; x < urls.length; x++ ){ String up = urls[x].getFile(); if ( up.endsWith(jarfile) ) return up; } return null; } /** * Helper method to quickly determine the type of error the compiler threw. This helps * us "guestimate" if the CFML developer is using a simple inline java or if they have * declared functions * * @param e * @return */ public boolean wasExpectingMethods(CharSequenceCompilerException e) { DiagnosticCollector<JavaFileObject> dC = e.getDiagnostics(); Iterator<Diagnostic<? extends JavaFileObject>> it = dC.getDiagnostics().iterator(); while ( it.hasNext() ){ Diagnostic<? extends JavaFileObject> d = it.next(); if ( d.getMessage(null).indexOf("class, interface, or enum expected") != -1 || d.getMessage(null).indexOf("<identifier> expected") != -1 ) return true; } return false; } }