Monday, July 16, 2012


Generating Java classes dynamically through Java compiler API

Some of the java coders around the world might have thought of an option to be able to compile a java source file dynamically.
To my surprise, almost at the end of Java 6 (I am expecting Java 7 to be out soon…), I noticed this feature under javax.tools package. May be I am the last one to notice this!! :-) .
This dynamic compiler API is included with Java 6 under javax.tools package.
How does it work?
javax.tools package has all the required interfaces and classes. Here, we will see how to compile a simple “HelloWorld” program source code stored in an in-memory String variable.
Able to compile a piece of source code stored in a string variable, WOW! this is interesting! isn’t it?
Follow the sequence of steps mentioned below. I explained these steps with the required code-snippets at that point. The full version of source code is available at the end of the article.
The most important classes in this API are,
  • JavaCompiler - This is used to create a compilation task
  • JavaCompiler.CompilationTask – The compilation task, on which we execute compile operation using it’s call method
  • JavaFileManager:Manages how the compiler read and writes to the files
  • JavaFileObject: The file object that abstracts the java source and class files
  • DiagnosticListener: This listens to the compilation diagnostic events
  • ToolProvider: Which is used to get the compiler object from the underlying platform.
We will discuss these classes further in the example below. Let’s start…
1. Build the source code to compile; we can read it from file system, retrieve from database, or generate it dynamically in memory!!
?
1
2
3
4
5
6
7
/**Java source code to be compiled dynamically*/
static String sourceCode = "package com.accordess.ca;" +
    "class DynamicCompilationHelloWorld{" +
        "public static void main (String args[]){" +
            "System.out.println (\"Hello, dynamic compilation world!\");" +
        "}" +
    "}" ;
2. Create a JavaFileObject instance for each of the compilation unit.
  • a) If the source is not from file system, then we need to write a class implementing fromJavaFileObject interface. Java 6 provides a sample implementation of this in the form of SimpleJavaFileObject. We can extend from this and customize it as per our needs.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
 * Creates a dynamic source code file object
 *
 * This is an example of how we can prepare a dynamic java source code for compilation.
 * This class reads the java code from a string and prepares a JavaFileObject
 *
 */
class DynamicJavaSourceCodeObject extends SimpleJavaFileObject{
    private String qualifiedName ;
    private String sourceCode ;
 
    /**
     * Converts the name to an URI, as that is the format expected by JavaFileObject
     *
     *
     * @param fully qualified name given to the class file
     * @param code the source code string
     */
    protected DynamicJavaSourceCodeObject(String name, String code) {
        super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
        this.qualifiedName = name ;
        this.sourceCode = code ;
    }
 
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors)
            throws IOException {
        return sourceCode ;
    }
 
    public String getQualifiedName() {
        return qualifiedName;
    }
 
    public void setQualifiedName(String qualifiedName) {
        this.qualifiedName = qualifiedName;
    }
 
    public String getSourceCode() {
        return sourceCode;
    }
 
    public void setSourceCode(String sourceCode) {
        this.sourceCode = sourceCode;
    }
}
Once you have this customized JavaFileObject implemented, make sure you create objects of this for each of your dynamic java source code entity.
?
1
2
3
/*Creating dynamic java source code file object*/
SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject ("com.accordess.ca.DynamicCompilationHelloWorld", sourceCode) ;
JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject} ;
  • b) If the source code is from file system, then create JavaFileObject instances from the File objects read from the file system.
?
1
2
3
4
/*Java source files read from file system*/
File []files = new File[]{file1, file2} ;
Iterableextends JavaFileObject> compilationUnits1 =
           fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files1));
3. Build a list of all your compilation units
?
1
2
/* Prepare a list of compilation units (java source code file objects) to input to compilation task*/
Iterableextends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);
4. If you need to provide any compilation options that you use in command line ‘javac’, such as ‘-d’, ‘-classpath’ and etc. Create a list of these options as a list of string objects.
?
1
2
3
4
/*Prepare any compilation options to be used during compilation*/
//In this example, we are asking the compiler to place the output files under bin folder.
String[] compileOptions = new String[]{"-d", "bin"} ;
Iterable compilationOptionss = Arrays.asList(compileOptions);
5. Now, retrieve the compiler from ToolProvider.
?
1
2
/*Instantiating the java compiler*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
This gets the compiler from the current platform.
6. As a next step, get a standard file manager from compiler, this file manager helps us to customize how a compiler reads and writes to files.
?
1
2
3
4
5
6
7
8
/**
 * Retrieving the standard file manager from compiler object, which is used to provide
 * basic building block for customizing how a compiler reads and writes to files.
 *
 * The same file manager can be reopened for another compiler task.
 * Thus we reduce the overhead of scanning through file system and jar files each time
 */
StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, Locale.getDefault(), null);
7. Create a diagnostic collector object, which collects the compilation problems.
?
1
2
/*Create a diagnostic controller, which holds the compilation problems*/
DiagnosticCollector diagnostics = new DiagnosticCollector();
8. Create a compilation task from compiler object by passing all of the above building blocks as input as required.
?
1
2
/*Create a compilation task from compiler by passing in the required input objects prepared above*/
CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, compilationOptionss, null, compilationUnits) ;
Let us spend some time understanding this line.
Here is the declaration of the above method from JavaCompiler class
?
1
2
3
4
5
6
CompilationTask getTask(Writer out,
                           JavaFileManager fileManager,
                           DiagnosticListenersuper JavaFileObject> diagnosticListener,
                           Iterable options,
                           Iterable classes,
                           Iterableextends JavaFileObject> compilationUnits);
out: is a writer object, if not null, this would be used to write all the compilation errors. If null, the errors are written to the standard error console, i.e., System.err.
fileManager: is an instance of the JavaFileManager, which abstracts programming language source and class files. In this context, file means an abstraction of regular files and other sources of data.
diagnosticListener: which acts as a listener to the compilation events happening and logs any issues found. The DiagnosticCollector, we have created here is impleting this listener and that stores all the compilation diagnostic messages. If this is not passed, the compilation issues will be logged on standard error console.
options: the compilation options to be passed during compilation, this can be null if there are no options to be used.
classes: class names (for annotation processing), null means no class names
compilationUnits: these are the list of JavaFileObject instances, that need to be compiled.
9. Finally, call the method ‘call’ on compilation task, which does the actual job, and returns ‘true’ on success or ‘false’ otherwise.
?
1
2
//Perform the compilation by calling the call method on compilerTask object.
boolean status = compilerTask.call();
10. On compilation failure, we can use the diagnostic collector to read the error messages and log them in specific format.
?
1
2
3
4
5
6
if (!status){//If compilation error occurs
    /*Iterate through each compilation problem and print it*/
    for (Diagnostic diagnostic : diagnostics.getDiagnostics()){
        System.out.format("Error on line %d in %s", diagnostic.getLineNumber(), diagnostic);
    }
}
If we need to compile another set of compilation units, just create another compilation task by passing the new set of compilation units and execute the call method on it.
Finally close the fileManager instance to flush out anything that is there in the buffer.
?
1
2
3
4
5
try {
            stdFileManager.close() ;//Close the file manager
    } catch (IOException e) {
            e.printStackTrace();
    }
that’s it. We are pretty much done with the example.  The piece of code mentioned over here doesn’t result into any errors. Introduce some error into the code string and play with it. By the way, after successful compilation the class file would be generated under the current folder if you don’t pass in the java options I mentioned over here. If you are passing the same java options I mentioned here, make sure you create a folder by the name ‘bin’ under your current folder. Otherwise this will result into an error.
Who will benefit from this feature?
Application server developers: Application server need to generate java files from JSP code and compile them dynamically, thus reducing the application hot deployment time.
IDEs and Developer Tools like Ant: This API helps them to load the compiler once and perform compilation as and when needed instead of loading an external compiler each time the code changes.
Here is the full-version of the example source code.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package com.accordess.ca;
 
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Locale;
import java.util.logging.Logger;
 
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
 
/**
 * A test class to test dynamic compilation API.
 *
 */
public class CompilerAPITest {
    final Logger logger = Logger.getLogger(CompilerAPITest.class.getName()) ;
 
    /**Java source code to be compiled dynamically*/
    static String sourceCode = "package com.accordess.ca;" +
        "class DynamicCompilationHelloWorld{" +
            "public static void main (String args[]){" +
                "System.out.println (\"Hello, dynamic compilation world!\");" +
            "}" +
        "}" ;
 
    /**
     * Does the required object initialization and compilation.
     */
    public void doCompilation (){
        /*Creating dynamic java source code file object*/
        SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject ("com.accordess.ca.DynamicCompilationHelloWorld", sourceCode) ;
        JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject} ;
 
        /*Instantiating the java compiler*/
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 
        /**
         * Retrieving the standard file manager from compiler object, which is used to provide
         * basic building block for customizing how a compiler reads and writes to files.
         *
         * The same file manager can be reopened for another compiler task.
         * Thus we reduce the overhead of scanning through file system and jar files each time
         */
        StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, Locale.getDefault(), null);
 
        /* Prepare a list of compilation units (java source code file objects) to input to compilation task*/
        Iterable compilationUnits = Arrays.asList(javaFileObjects);
 
        /*Prepare any compilation options to be used during compilation*/
        //In this example, we are asking the compiler to place the output files under bin folder.
        String[] compileOptions = new String[]{"-d", "bin"} ;
        Iterable compilationOptionss = Arrays.asList(compileOptions);
 
        /*Create a diagnostic controller, which holds the compilation problems*/
        DiagnosticCollector diagnostics = new DiagnosticCollector();
 
        /*Create a compilation task from compiler by passing in the required input objects prepared above*/
        CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, compilationOptionss, null, compilationUnits) ;
 
        //Perform the compilation by calling the call method on compilerTask object.
        boolean status = compilerTask.call();
 
        if (!status){//If compilation error occurs
            /*Iterate through each compilation problem and print it*/
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()){
                System.out.format("Error on line %d in %s", diagnostic.getLineNumber(), diagnostic);
            }
        }
        try {
            stdFileManager.close() ;//Close the file manager
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String args[]){
        new CompilerAPITest ().doCompilation() ;
    }
 
}
 
/**
 * Creates a dynamic source code file object
 *
 * This is an example of how we can prepare a dynamic java source code for compilation.
 * This class reads the java code from a string and prepares a JavaFileObject
 *
 */
class DynamicJavaSourceCodeObject extends SimpleJavaFileObject{
    private String qualifiedName ;
    private String sourceCode ;
 
    /**
     * Converts the name to an URI, as that is the format expected by JavaFileObject
     *
     *
     * @param fully qualified name given to the class file
     * @param code the source code string
     */
    protected DynamicJavaSourceCodeObject(String name, String code) {
        super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
        this.qualifiedName = name ;
        this.sourceCode = code ;
    }
 
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors)
            throws IOException {
        return sourceCode ;
    }
 
    public String getQualifiedName() {
        return qualifiedName;
    }
 
    public void setQualifiedName(String qualifiedName) {
        this.qualifiedName = qualifiedName;
    }
 
    public String getSourceCode() {
        return sourceCode;
    }
 
    public void setSourceCode(String sourceCode) {
        this.sourceCode = sourceCode;
    }
}
I have also attached the eclipse project to this post. You can download and import this project into Eclipse IDE. You will be ready to play with it…!
Download the source code eclipse project here

2 comments:

Aniyanewjackson said...

Air Jordan 6 Retro Black Infrared 2019, PTSD can occur at any age, including childhood. The disorder can be accompanied by depression, substance abuse, or anxiety. Symptoms may be mild or severe people may become easily irritated or have violent outbursts.

Air Jordan 1 Mid Gs Black Orange Purple, Outside of full blown fashion brands, some more obscure trends did make their way into the fashion mainstream. Sweatbands influenced by St. Louis rapper Nelly, which had a bizarre crossover appeal, became a sports accessory that young men white and black started wearing on completely non athletic occasions..

Jordan 13 Black Red For Sale, But it's nothing special. And women have little presence here. Golshifteh Farahani (About Elly) is the most high profile of the lot, but her talents are wasted on Extraction, as is the case with Painyuli, who orders people around and looks through binoculars, or Tripathi, whose role amounts to a cameo.

Maa.. {tag: White Yeezy Boost 350 Ebay}Air Jordan 1 Black Toe Womens, If you choose not to provide data required to provide you with a product or feature, you cannot use that product or feature. Likewise, where we need to collect personal data by law or to enter into or carry out a contract with you, and you do not provide the data, we will not be able to enter into the contract; or if this relates to an existing product you're using, we may have to suspend or cancel it. We will notify you if this is the case at the time.

Cooper recorded his fourth 1,000 yard receiving season in 2019. {tag: When Did The White Yeezys Come Out}Jordan Retro 4 Blue Black, High school he was a very nice guy and would help any way he could, wrote Scott Balser. Never know what others go though in life that makes them make certain decisions. 1986 grad book for Riverview High included an entry noting he could be seen cruising around on one wheel of his Honda motorcycle.

hhh said...

A single ask assure upto 20 move gets under way also it able to figure this comes to pouring down (Ray Ban Outlet Store) rain since raining, As soon as it truly beautiful and / or possibly frigidly frozen(It all scored to save hopeless 20 degrees fahrenheit Fahrenhe). Battery power facets all (Ray Ban Outlet) that Noco minutes a form, Which will help patients abstain climate an incorrect connection or just achieving a contacts of curiosity. Furthermore, it features personal computer usb port with regards to asking and requirements simply just 3 tons to completely (Coach Outlet Store Online) fee..

A absorbs, While they care. Regarding that use costs, The middle offers floral instruments with the help (Cheap Jordan Shoes Websites) of veterinarians in the community along with free of charge spaying together (Ray Ban New Wayfarer Polarized) with neutering, As (New Jordan Releases 2020) well as family pets include latest attached to techniques. Five years and long time right after, (Cheap Yeezy Shoes Sale) We nevertheless (Michael Kors Outlet Online) happier to the middle relating to