I am using JavaCompiler
of javax.tools
to compile some java code and I am trying to use wildcard in my classpath
in order to include all the .jar
files but I fail.
Here is my code:
String classpath = "C:\tomcat6\webapps\myapp/WEB-INF/lib/javax.ws.rs-api-2.0-m10.jar;"
+ "C:\\tomcat6\\webapps\\myapp/WEB-INF/lib/javax.persistence-2.1.0.jar";
Iterable<String> options = Arrays.asList("-d", classesBaseDir,
"-classpath", classpath);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager,
diagnostics, options, null, file);
boolean result = task.call();
The code above works just fine. But when I am trying to change the classpath
to
String classpath = "C:\\tomcat6\\webapps\\myapp/WEB-INF/lib/*";
it fails with
compiler.err.doesnt.exist|package javax.ws.rs does not exist
...
symbol: class GET
location: class com.my.oasis.resources.TestClass
09/04/2014 14:27:09:030 | COMPILER_DIAGNOSTIC | compileResource() - compiler.err.cant.resolve.location|cannot find symbol
...
I have also tried the following alterations
String classpath = "\"C:\\tomcat6\\webapps\\myapp/WEB-INF/lib/*\"";
String classpath = "'C:\\tomcat6\\webapps\\myapp/WEB-INF/lib/*'";
but none of them worked. Any ideas?
Thanks
Note: the reason why the path includes slashes and backslashes is because the my program identifies the environment in runtime and auto completes the path.
Edit: I am using tomcat 6 and java 1.7.0_21
Wildcards: Since Java 1.6 wildcards are supported when using java/javaw/javac, more information: Windows/Solaris and Linux
example:
javac -cp "lib/*" Test.java
This uses all .jar files (not .class!) in the lib directory as classpath. This should not be confused with the *-expansion of your shell. -cp lib/*
gets expanded to -cp lib/a.jar lib/b.jar
which is not valid argument syntax. In order to avoid this you have to add quotation marks: -cp "lib/*"
The cause of your Problem: You are trying to call the Java compiler from source directly with its Java API. This source code does not contain the wildcard expansion.
The JDK ships with a wrapper binary (javac,javadoc,javah,javap are all the same binary) which does some things and finally calls the compiler task. This wrapper also expands the wildcards in your classpath and therefore the compiler task doesn't have to do this anymore (and it doesn't). See at Compiler Readme section "build -> Notes -> The launcher". Launcher sourcecode.
Solution:
- A very poor solution would be to call
javac
through a Processbuilder. (This is not recommended since it is a complicated and error prone solution for a simple problem)
- Expand the wildcards yourself:
example code:
String classpath = buildClassPath("lib/", "test/", "lib/*");
System.out.println(classpath);
// output: lib/;test/;lib/a.jar;lib/b.jar;
This function takes all classpath entries and builds one classpath. Classpath entries with a wildcard in it will get expanded.
/**
* This function builds a classpath from the passed Strings
*
* @param paths classpath elements
* @return returns the complete classpath with wildcards expanded
*/
private static String buildClassPath(String... paths) {
StringBuilder sb = new StringBuilder();
for (String path : paths) {
if (path.endsWith("*")) {
path = path.substring(0, path.length() - 1);
File pathFile = new File(path);
for (File file : pathFile.listFiles()) {
if (file.isFile() && file.getName().endsWith(".jar")) {
sb.append(path);
sb.append(file.getName());
sb.append(System.getProperty("path.separator"));
}
}
} else {
sb.append(path);
sb.append(System.getProperty("path.separator"));
}
}
return sb.toString();
}
Using backslashes or slashes makes no difference. But you obviously assume that the path is auto-globbed (like a normal command line would). This does not happen. So you run your compiler as you would with a command line arg of
-classpath 'C:\\tomcat6\\webapps\\myapp/WEB-INF/lib/*'
which is not what you want. You may look into the API docs for java.io.File.listFiles(FileFilter)
or even java.nio.file.Files.walkFileTree(Path, FileVisitor)
to gain a better understanding.
To expand a bit on that, when your shell sees C:\tomcat6\webapps\myapp/WEB-INF/lib/* it expands it into a space-separated list of whatever is in your WEB-INF/lib
directory. This is called globbing, or since it's done automatically, auto-globbing.
Now Java doesn't do that, but you can build it yourself in a few lines of code:
StringBuilder sb = new StringBuilder();
String[] filenames = new File("C:\\tomcat6\\webapps\\myapp/WEB-INF/lib/").list();
for(int i = 0; i < filenames.length; i++) {
if(i > 0) sb.append(File.pathSeparatorChar); // separate with ':' or ';' on Win
sb.append(filenames[i]); // append the filename
}
Iterable<String> options = Arrays.asList("-d", classesBaseDir, "-cp", sb.toString())
...
Since Java1.7 you can also use Files.newDirectoryStream(Path)
instead of list(File)
. With 1.8 you could even call join
instead of joining manually.