Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaCompiler with custom ClassLoader and FileManager

I wish to compile source without having the dependencies present on the machine.
Example: File A.java:

import some.pkg.B; 
public class A extends B {...}

I don't have B source present, I wish to hook either JavaFileManager or a custom ClassLoader in order to get the symbols in question (the package 'some.package' and class B) and then use a service I have that retrieves the source string.

The compiling code: (inputFiles has A.java)

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
CustomClassLoader classLoader = new CustomClassLoader();
StandardJavaFileManager standardfileManager = compiler.getStandardFileManager(this, null, null);
JavaFileManager fileManager = new CustomFileManager(standardfileManager, output, classLoader);
CompilationTask task = compiler.getTask(null, fileManager, this, null, null, inputFiles);
boolean result = task.call();

Hooks on JavaFileManager (getFileForInput..) and on my classloader (findClass, loadClass ..) didn't triggered when compiling and I got error messages:

A.java:#: package some.pkg does not exist
A.java:#: cannot find symbol
symbol: class B

EDIT

After playing around with the API, going over JavaCompiler (older version) source and reading Compilation Overview I still can't find an API hook I can use to provide me Symbols from the syntax trees. It seems that the API needs to get all resources based on package names as suggested by kschneid.
One workaround I thought about is running the JavaCompiler and analyze the error messages for missing symbols. That way I will know which symbols are needed, get them and recompile.
Any other workarounds / solutions?

like image 707
Chen Harel Avatar asked May 26 '12 14:05

Chen Harel


2 Answers

(I assume that you're not really using a package name of "package" since that would just be illegal...)

Your custom JavaFileManager should be getting its list method invoked. Hopefully this notation makes sense, but the combination of args to that method should look like this:

[PLATFORM_CLASS_PATH, some, [CLASS], false]
[CLASS_PATH, some, [SOURCE, CLASS], false]
[PLATFORM_CLASS_PATH, some.pkg, [CLASS], false]
[CLASS_PATH, some.pkg, [SOURCE, CLASS], false]

I'm not sure how difficult it will be for your specific environment to create the appropriate Iterable<JavaFileObject> instances, but I think that's what would be required...

like image 98
kschneid Avatar answered Oct 09 '22 06:10

kschneid


I've found that a nice way to hook in classes at compile time is with Groovy AST Transformation. You can have a look at what can be done here

That's not plain old java though, but it can be an handy tool to know

like image 32
Grooveek Avatar answered Oct 09 '22 05:10

Grooveek