4
votes

I have an existing "Example Webapp" that references "Example Library" using Maven. I'm running Tomcat 7 inside Eclipse 4.3RC3 with the m2e plugin. When I launch Example Webapp on Tomcat inside Eclipse, I have verified that the example-library.jar is probably getting deployed in the Tomcat instance's WEB-INF/lib folder.

The Example Webapp has code that compiles certain classes on the fly using JavaCompiler.CompilationTask. These dynamically generated classes reference classes in example-library.jar. Unfortunately the compile task is failing because the referenced classes cannot be found.

I understand that I can set the JavaCompiler classpath, but System.getProperty("java.class.path") only returns me the Tomcat classpath, not the webapp classpath:

C:\bin\tomcat\bin\bootstrap.jar;C:\bin\tomcat\bin\tomcat-juli.jar;C:\bin\jdk6\lib\tools.jar

Other have said that I need to get the real path of WEB-INF/lib from the servlet context, but the class generation code doesn't know anything about a servlet context --- it is written to be agnostic of whether it is used on the client or on the server.

In another question, one answer indicated I could enumerate the classloader URLs, and sure enough this provides me with the jars in WEB-INF/lib, but when I provide this as a -classpath option to compiler.getTask(), the task still fails because it can't find the referenced classes.

How can I simply provide the classpath of the currently executing code to the JavaCompiler instance so that it will find the classes from the libraries in WEB-INF/lib? (A similar question was raised but never answered regarding referencing jars within ear files using JavaCompiler.)

Example: In an attempt to get things working at any cost, I even tried to hard-code the classpath. For example, I have foobar.lib in my webapp lib directory, so I used the following code, modified from the answers I indicated above:

List<String> options = new ArrayList<String>();
options.add("-classpath");
options.add("C:\\work\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp0\\wtpwebapps\\FooBar\\WEB-INF\\lib\\foobar.jar");
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
boolean success = task.call();

In the end success is false, and my diaognostics indicates package com.example.foo.bar does not exist..., even though that package is in foobar.jar.

3

3 Answers

1
votes

Put example-library.jar somewhere in your file system and pass that location to the code that runs JavaCompiler (the -classpath option). If you use an exploded WAR file to deploy, you can of course point it to the physical location within the WEB-INF/lib folder. The point is that you only need one configurable parameter in your webapp to do this, which can be a properties file entry, -D system property, database row or something else entirely.

Sample code (tested in Tomcat 7 and OpenJDK 1.7 on Fedora 18 x64):

private File compile(File javaFile, String classpath) throws IOException {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjects(javaFile);
    List<String> options = classpath != null ? Arrays.asList("-classpath", classpath) : null;
    StringWriter output = new StringWriter();
    try {
        boolean successful = compiler.getTask(output, fileManager, null, options, null, compilationUnit).call();
        if (!successful) {
            throw new CompilationException("Failed to compile: " + javaFile, output.toString());
        }
        return firstClassFileFrom(javaFile.getParentFile());
    } finally {
        fileManager.close();
    }
}

private File firstClassFileFrom(File directory) {
    return directory.listFiles(new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
            return name.endsWith(".class");
        }
    })[0];
}

See https://github.com/jpalomaki/compiler for a runnable sample webapp.

0
votes

i met the same question. The reason is not "-classpath" .

my code :

   String classpath ="xx/WEB-INF/clases ";
   List<String> options = classpath != null ? Arrays.asList("-d", classpath,"-cp",classpath) : null;
   CompilationTask task = compiler.getTask(null, javaDinamicoManager, diagnostics,
          options, null, Arrays.asList(sourceObj));
   boolean result = task.call();

the “result” will return true .

0
votes

You could follow the answer provided for the even more specific question of how to load dependencies of compiled code from within a web app running directly from an unexpanded WAR file (there are no JAR files to reference - only the container's class loder knows how to access the classes): https://stackoverflow.com/a/45038007/2546679