public class JspCompilationInfo {

    private String classFileLocation;
    private String jspSourceLocation;
    private String jspClassName;
    private String javaSourceLocation;
    private String jspPath;
    private String classOutputDir;
    private String webRoot;
    private Map taglibs;
    private static Map compilationHistory = new HashMap();

    public JspCompilationInfo() {
        this.taglibs = new HashMap();
    }

    private String absolute(String path) {
        return new File(path).getAbsolutePath();
    }

    public String toString() {
        StringBuffer s = new StringBuffer();
        s.append("{").append(getClass().getName());
        s.append("\n jsp path:   ").append(getJspPath());
        s.append(",\n web root:   ").append(getWebRoot());
        s.append(",\n jsp file:   ").append(getJspSource());
        s.append(",\n java file:  ").append(getJavaSource());
        s.append(",\n class file: ").append(getClassFile());
        s.append(",\n class name: ").append(getClassName());
        s.append(",\n output dir: ").append(getClassOutputDir());
        s.append("\n}");
        return s.toString();
    }

    public String getClassName() {
        return jspClassName;
    }

    public void setClassName(String jspClassName) {
        this.jspClassName = jspClassName;
    }

    public String getClassFile() {
        return classFileLocation;
    }

    public void setClassFile(String classFileLocation) {
        this.classFileLocation = absolute(classFileLocation);
    }

    public String getJspSource() {
        return jspSourceLocation;
    }

    public void setJspSource(String jspSourceLocation) {
        this.jspSourceLocation = absolute(jspSourceLocation);
    }

    public String getJavaSource() {
        return javaSourceLocation;
    }

    public void setJavaSource(String javaSourceLocation) {
        this.javaSourceLocation = absolute(javaSourceLocation);
    }

    public String getJspPath() {
        return jspPath;
    }

    public void setJspPath(String jspPath) {
        this.jspPath = jspPath;
    }

    public String getClassOutputDir() {
        return classOutputDir;
    }

    public void setClassOutputDir(String outputDir) {
        this.classOutputDir = absolute(outputDir);
    }

    public String getWebRoot() {
        return webRoot;
    }

    public void setWebRoot(String webRoot) {
        this.webRoot = webRoot;
    }

    public Map getTaglibs() {
        return taglibs;
    }

    public void setTaglibs(Map taglibs) {
        this.taglibs = new HashMap(taglibs);
    }

    public synchronized void compilationWasSuccessful() {
        compilationHistory.put(jspSourceLocation, new LastCompile(taglibs));
    }

    public synchronized boolean jspCompilationRequired() {
        // TODO: only avoid compilation if there is a matching MD5 for the .jsp
        // file in a cache or if the file is missing altogether.
        File jsp = new File(jspSourceLocation);
        File java = new File(javaSourceLocation);
        File clazz = new File(classFileLocation);
        return (doesNotExistOrIsTooOld(clazz) || doesNotExistOrIsTooOld(java)
                || jsp.lastModified() > java.lastModified() || taglibsHaveChangedSinceLastCompile());
    }

    private boolean taglibsHaveChangedSinceLastCompile() {
        LastCompile lastCompile = (LastCompile) compilationHistory.get(getJspSource());
        if (lastCompile == null) {
            return true;
        }
        return !lastCompile.getTaglibs().equals(getTaglibs());
    }

    private boolean doesNotExistOrIsTooOld(File file) {
        long expirationAge = 5 * 60 * 1000L;
        long expirationThreshold = System.currentTimeMillis() - expirationAge;
        return file.exists() == false || file.lastModified() < expirationThreshold;
    }

    /**
     * Represents the tag configuration of a prior compilation.
     * 
     * @author Meinert Schwartau (scwar32)
     */
    private static final class LastCompile {

        private final Map taglibs;

        public LastCompile(Map taglibs) {
            this.taglibs = taglibs;
        }

        public Map getTaglibs() {
            return taglibs;
        }
    }
}
 public class CommandLineJavac implements JavaCompiler {

    private static final String SEPARATOR = System.getProperty("path.separator");

    public boolean compile(String srcFile, String destDir, String[] classpath) throws Exception {
        srcFile = new File(srcFile).getCanonicalPath();
        String cp = join(classpath);
        String[] command = buildCommandLine(srcFile, destDir, cp);
        return execute(command);
    }

    protected String[] buildCommandLine(String pathToJavaSource, String outputDirectory,
            String classpathString) {
        return new String[] { "javac", "-classpath", classpathString, "-d", outputDirectory,
                pathToJavaSource };
    }

    private String join(String[] classpath) {
        StringBuffer s = new StringBuffer(5000);
        for (int i = 0; i < classpath.length; i++) {
            if (s.length() > 0) {
                s.append(SEPARATOR);
            }
            s.append(classpath[i]);
        }
        return s.toString();
    }

    protected boolean execute(String[] commandLine) throws IOException, InterruptedException {
        Process p = Runtime.getRuntime().exec(commandLine);
        String processOutput = readOutput(p);
        boolean success = (p.waitFor() == 0);
        if (!success) {
            System.err.println(processOutput);
        }
        return success;
    }

    private String readOutput(final Process p) throws IOException {
        try {
            StringWriter output = new StringWriter();
            final PrintWriter ps = new PrintWriter(output);
            Thread stderrThread = new Thread(new StreamConsumer("STDERR", p.getErrorStream(), ps));
            Thread stdoutThread = new Thread(new StreamConsumer("STDOUT", p.getInputStream(), ps));
            p.getOutputStream().close();
            stderrThread.start();
            stdoutThread.start();
            stderrThread.join();
            stdoutThread.join();
            return output.toString();
        } catch (Exception e) {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            return sw.toString();
        }
    }

    public boolean isAvailable() {
        try {
            Process p = Runtime.getRuntime().exec("javac");
            String s = readOutput(p);
            return s.indexOf("Usage: javac") != -1;
        } catch (Exception e) {
            return false;
        }
    }
}
 public class Java6Compiler extends CommandLineJavac {
	private static final String TMPDIR = System.getProperty("java.io.tmpdir");

	protected boolean execute(String[] arguments) throws IOException,
			InterruptedException {
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		try {
			return executeWithReflection(arguments, output) == 0;
		} finally {
			File logFile = new File(TMPDIR, getClass().getName() + ".log");
			IO.append(output.toString(), logFile);
		}
	}

	private int executeWithReflection(String[] arguments,
			ByteArrayOutputStream output) {
		try {
			Object systemJavaCompiler = acquireCompilerImplementation();
			Method[] methods = systemJavaCompiler.getClass().getMethods();
			for (int i = 0; i < methods.length; i++) {
				if (methods[i].getName().equals("run")) {
					Object returnValue = methods[i].invoke(systemJavaCompiler,
							new Object[] {
									new ByteArrayInputStream(new byte[0]),
									output, output, arguments });
					return Integer.parseInt(String.valueOf(returnValue));
				}
			}
			return -1;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	protected String[] buildCommandLine(String pathToJavaSource,
			String outputDirectory, String classpathString) {
		return new String[] { "-verbose", "-classpath", classpathString, "-d",
				outputDirectory, pathToJavaSource };
	}

	public boolean isAvailable() {
		return acquireCompilerImplementation() != null;
	}

	private Object acquireCompilerImplementation() {
		try {
			Class factory = Class.forName("javax.tools.ToolProvider");
			Method method = factory.getMethod("getSystemJavaCompiler", null);
			return method.invoke(null, null);
		} catch (Exception e) {
			return null;
		}
	}
} public interface JavaCompiler {

    /**
     * Compiles the given .java source file into the given .class file using an explicitly set class
     * path.
     * 
     * @param pathToJavaSource
     *            Path to the source file to compile.
     * @param outputDirectory
     *            Path to the output directory under which to generate the .class file
     * @param classpath
     *            The class path to use for compilation.
     */
    boolean compile(String pathToJavaSource, String outputDirectory, String[] classpath)
            throws Exception;

    /**
     * Indicates whether this Java compiler implementation is available on the current system.
     */
    boolean isAvailable();
}
 public class SunJavaC implements JavaCompiler {

    private static final String SEPARATOR = System.getProperty("path.separator");

    public boolean compile(String pathToJavaSource, String outputDirectory, String[] classpath)
            throws Exception {
        String classpathString = join(classpath);
        String[] args = buildArgs(pathToJavaSource, outputDirectory, classpathString);
        return compile(args);
    }

    private boolean compile(String[] args) {
        try {
            Class compilerClass = getCompileClass();
            Method compilerMethod = resolveCompilerMethod(compilerClass);
            Object compilerInstance = compilerClass.newInstance();
            Object[] compilerArguments = new Object[] { args };
            int returnCode = ((Integer) compilerMethod.invoke(compilerInstance, compilerArguments))
                    .intValue();
            return returnCode == 0;
        } catch (Throwable e) {
            System.err.println(e.getMessage());
            return false;
        }
    }

    private String[] buildArgs(String pathToJavaSource, String outputDirectory,
            String classpathString) {
        return new String[] { "-classpath", classpathString, "-d", outputDirectory,
                pathToJavaSource };
    }

    private String join(String[] classpath) {
        StringBuffer s = new StringBuffer(5000);
        for (int i = 0; i < classpath.length; i++) {
            if (s.length() > 0) {
                s.append(SEPARATOR);
            }
            s.append(classpath[i]);
        }
        return s.toString();
    }

    public boolean isAvailable() {
        try {
            resolveCompilerMethod(getCompileClass());
            return true;
        } catch (Throwable ex) {
            return false;
        }
    }

    private Method resolveCompilerMethod(Class compilerClass) throws NoSuchMethodException {
        return compilerClass.getMethod("compile", new Class[] { (new String[0]).getClass() });
    }

    private Class getCompileClass() throws ClassNotFoundException {
        return Class.forName("com.sun.tools.javac.Main");
    }
} public class MockOptions implements Options {

    private Options options;
    private ServletContext servletContext;
    private JspCompilationInfo compilationInfo;

    public MockOptions(Options options, ServletContext context, JspCompilationInfo compilationInfo) {
        this.options = options;
        this.servletContext = context;
        this.compilationInfo = compilationInfo;
    }

    public boolean getErrorOnUseBeanInvalidClassAttribute() {
        return options.getErrorOnUseBeanInvalidClassAttribute();
    }

    public boolean getKeepGenerated() {
        return options.getKeepGenerated();
    }

    public boolean isPoolingEnabled() {
        return options.isPoolingEnabled();
    }

    public boolean getMappedFile() {
        return options.getMappedFile();
    }

    public boolean getSendErrorToClient() {
        return options.getSendErrorToClient();
    }

    public boolean getClassDebugInfo() {
        return options.getClassDebugInfo();
    }

    public int getCheckInterval() {
        return options.getCheckInterval();
    }

    public boolean getDevelopment() {
        return options.getDevelopment();
    }

    public boolean isSmapSuppressed() {
        return true; // options.isSmapSuppressed();
    }

    public boolean isSmapDumped() {
        return false; // options.isSmapDumped();
    }

    public boolean getTrimSpaces() {
        return options.getTrimSpaces();
    }

    public String getIeClassId() {
        return options.getIeClassId();
    }

    public File getScratchDir() {
        return options.getScratchDir();
    }

    public String getClassPath() {
        return options.getClassPath();
    }

    public String getCompiler() {
        return options.getCompiler();
    }

    public String getCompilerTargetVM() {
        return options.getCompilerTargetVM();
    }

    public String getCompilerSourceVM() {
        return options.getCompilerSourceVM();
    }

    public TldLocationsCache getTldLocationsCache() {
        TldLocationsCache realCache = options.getTldLocationsCache();
        return new MockTldLocationsCache(realCache, servletContext);
    }

    public String getJavaEncoding() {
        return options.getJavaEncoding();
    }

    public boolean getFork() {
        return options.getFork();
    }

    public JspConfig getJspConfig() {
        return options.getJspConfig();
    }

    public boolean isXpoweredBy() {
        return options.isXpoweredBy();
    }

    public TagPluginManager getTagPluginManager() {
        return new MockTagPluginManager(servletContext, options.getTagPluginManager(),
                compilationInfo.getTaglibs());
    }

    public boolean genStringAsCharArray() {
        return options.genStringAsCharArray();
    }

    public int getModificationTestInterval() {
        return options.getModificationTestInterval();
    }

    public boolean isCaching() {
        return options.isCaching();
    }

    public Map getCache() {
        return options.getCache();
    }
}
 public class MockServletConfig implements ServletConfig {

    private ServletContext servletContext;

    public MockServletConfig(ServletContext context) {
        this.servletContext = context;
    }

    public String getServletName() {
        return "MyServlet";
    }

    public ServletContext getServletContext() {
        return servletContext;
    }

    public String getInitParameter(String name) {
        return null;
    }

    public Enumeration getInitParameterNames() {
        return new Vector().elements();
    }
}
 public class MockTagInfo extends TagInfo {

    public MockTagInfo() {
        super("", "", "", "", null, null, null);
    }

    public String getTagClassName() {
        try {
            throw new RuntimeException("MockTagInfo being asked for getTagClassName()");
        } catch (RuntimeException e) {
            e.printStackTrace(System.out);
        }
        return super.getTagClassName();
    }
}
 public class Path extends ArrayList {

    private static final String SEPARATOR = System.getProperty("path.separator");

    public void addSystemProperty(String name) {
        String property = System.getProperty(name);
        if (property != null) {
            String[] systemClasspath = property.split(SEPARATOR);
            for (int i = 0; i < systemClasspath.length; i++) {
                String entry = systemClasspath[i];
                File file = new File(entry);
                if (file.exists()) {
                    add(file.getAbsolutePath());
                } else {
                    add(entry);
                }
            }
        }
    }

    public boolean add(Object pathElement) {
        if (contains(pathElement)) {
            return false;
        }
        return super.add(pathElement);
    }

    public boolean add(File file) {
        return add(file.getAbsolutePath());
    }

    public String[] toStringArray() {
        return (String[]) toArray(new String[size()]);
    }

    public void addContainer(Class klass) {
        String resource = resourcePathFor(klass);
        addJarFile(klass.getResource(resource));
        try {
            ClassLoader context = klass.getClassLoader();
            if (context != null) {
                Enumeration en = context.getResources(resource);
                while (en.hasMoreElements()) {
                    addJarFile((URL) en.nextElement());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String resourcePathFor(Class klass) {
        String resource = klass.getName().replaceAll("\\.", "/");
        return "/" + resource + ".class";
    }

    private void addJarFile(URL url) {
        if (url != null) {
            addJarFile(url.toExternalForm());
        }
    }

    private void addJarFile(String url) {
        String prefix = "jar:file:";
        if (url.startsWith(prefix)) {
            String file = url.substring(prefix.length());
            if (file.indexOf("!") > -1) {
                file = file.substring(0, file.indexOf('!'));
            }
            add(new File(file));
        }
    }
} public class Strings {

    /**
     * Replace instances of "what" from "fromWhere" with "withWhat". This is essentially a
     * replacement for not being able to use {@link String#replace(String, String, String)} with JDK
     * 1.4.
     * 
     * @param fromWhere
     *            The string to replace stuff from.
     * @param what
     *            The string to replace.
     * @param withWhat
     *            The string to replace with.
     * @return A new string with the replaced content.
     */
    public static String replace(String fromWhere, String what, String withWhat) {
        if (what.length() > 0) {
            int indexOfWhat = fromWhere.indexOf(what);
            if (indexOfWhat != -1) {
                String beforeMatch = fromWhere.substring(0, indexOfWhat);
                String afterMatch = fromWhere.substring(indexOfWhat + what.length());
                fromWhere = beforeMatch + withWhat + replace(afterMatch, what, withWhat);
            }
        }
        return fromWhere;
    }
}
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.jsp.tagext.TagInfo;
import net.sf.jsptest.compiler.JspCompilationInfo;
import net.sf.jsptest.compiler.java.CommandLineJavac;
import net.sf.jsptest.compiler.java.Java6Compiler;
import net.sf.jsptest.compiler.java.JavaCompiler;
import net.sf.jsptest.compiler.java.SunJavaC;
import net.sf.jsptest.compiler.jsp20.mock.MockOptions;
import net.sf.jsptest.compiler.jsp20.mock.MockServletConfig;
import net.sf.jsptest.compiler.jsp20.mock.MockTagInfo;
import net.sf.jsptest.utils.Path;
import net.sf.jsptest.utils.Strings;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jasper.EmbeddedServletOptions;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.Options;
import org.apache.jasper.compiler.AntCompiler;
import org.apache.jasper.compiler.Compiler;
import org.apache.jasper.compiler.JspRuntimeContext;
import org.apache.jasper.compiler.ServletWriter;
import org.apache.jasper.servlet.JspCServletContext;
import org.apache.jasper.servlet.JspServletWrapper;


    private void createPathToGeneratedJavaSource(JspCompilationInfo info) {
        new File(info.getJavaSource()).getParentFile().mkdirs();
    }

    private JspServletWrapper makeWrapper(ServletContext servletContext, Options options,
            JspRuntimeContext jspRuntimeContext) throws MalformedURLException, JasperException {
        TagInfo tagInfo = new MockTagInfo();
        String tagFilePath = "/";
        URL tagFileJarUrl = new File(".").toURL();
        JspServletWrapper wrapper = new JspServletWrapper(servletContext, options, tagFilePath,
                tagInfo, jspRuntimeContext, tagFileJarUrl);
        return wrapper;
    }

    private Options createOptions(ServletContext ctx, ServletConfig cfg, JspCompilationInfo info) {
        Options options = new EmbeddedServletOptions(cfg, ctx);
        return new MockOptions(options, ctx, info);
    }

    private void resolveJavaSourceFile(JspCompilationInfo info) {
        File dir = new File(info.getClassOutputDir());
        if (getJspPackageName().length() > 0) {
            dir = new File(dir, getJspPackageName().replace('.', '/'));
        }
        dir.mkdirs();
        String name = resolveJavaSourceFileName(info.getJspPath());
        info.setJavaSource(new File(dir, name).getPath());
    }

    private String resolveJavaSourceFileName(String jspPath) {
        String name = encodeSpecialCharacters(jspPath);
        if (name.startsWith("/")) {
            name = name.substring(1);
        }
        return name + ".java";
    }

    private String encodeSpecialCharacters(String name) {
        StringBuffer result = new StringBuffer();
        char[] chars = name.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '-') {
                result.append("_002d");
            } else if (chars[i] == '_') {
                result.append("_005f");
            } else if (chars[i] == '.') {
                result.append("_");
            } else {
                result.append(chars[i]);
            }
        }
        return result.toString();
    }

    private String resolveJspSourceFile(String jsp) {
        if (jsp.startsWith("/")) {
            jsp = jsp.substring(1);
        }
        return new File(getWebRoot(), jsp).getPath();
    }

    private void resolveClassName(JspCompilationInfo info) {
        String baseName = new File(info.getJavaSource()).getName();
        baseName = baseName.substring(0, baseName.indexOf("."));
        String packageName = getPackagePrefix() + getSubDirPackagePrefix(info);
        info.setClassName(packageName + baseName);
    }

    private String getPackagePrefix() {
        String packagePrefix = getJspPackageName();
        if (packagePrefix != null && packagePrefix.length() > 0) {
            packagePrefix = packagePrefix + ".";
        }
        return packagePrefix;
    }

    private String getSubDirPackagePrefix(JspCompilationInfo info) {
        String dirPrefix = info.getJspPath();
        if (dirPrefix.startsWith("/")) {
            dirPrefix = dirPrefix.substring(1);
        }
        int lastSlashIndex = dirPrefix.lastIndexOf("/");
        if (lastSlashIndex != -1) {
            dirPrefix = dirPrefix.substring(0, lastSlashIndex);
            dirPrefix = encodeSpecialCharacters(dirPrefix);
            dirPrefix = dirPrefix.replace('/', '.') + ".";
        } else {
            dirPrefix = "";
        }
        return dirPrefix;
    }

    private void compileJavaToBytecode(JspCompilationInfo info) throws Exception {
        File classFile = new File(info.getClassFile());
        classFile.delete();
        logCompilation(info.getJavaSource(), info.getClassOutputDir());
        boolean ok = javac()
                .compile(info.getJavaSource(), info.getClassOutputDir(), getClassPath());
        assertTrue("javac failed", ok);
        assertTrue("Failed to compile .java file to " + classFile.getAbsolutePath(), classFile
                .exists());
    }

    private String[] getClassPath() {
        Path cp = new Path();
        cp.addSystemProperty("java.class.path");
        cp.addContainer(javax.servlet.jsp.tagext.JspTag.class);
        cp.addContainer(javax.servlet.jsp.jstl.core.LoopTag.class);
        cp.addContainer(javax.servlet.http.HttpServlet.class);
        cp.addContainer(org.apache.taglibs.standard.Version.class);
        cp.addContainer(org.apache.jasper.JspC.class);
        cp.addContainer(org.apache.jasper.compiler.Compiler.class);
        cp.addContainer(org.apache.jasper.runtime.HttpJspBase.class);
        cp.add(new File("target", "test-classes").getAbsolutePath());
        cp.add(new File("target", "classes").getAbsolutePath());
        return cp.toStringArray();
    }

    private void resolveClassFileLocation(JspCompilationInfo info) {

        String file = Strings.replace(info.getJavaSource(), ".java", ".class");
