4

I have been trying to compile some Java Classes in a String using java. I have used javax.tools.JavaCompiler to compile the Classes in the Strings.

I have made instances of SimpleJavaFileObject by a Subclass that I have made of SimpleJavaFileObject.

import javax.tools.SimpleJavaFileObject;
import java.net.URI;

public class JavaSourceFromString extends SimpleJavaFileObject {
     final String code;

    public JavaSourceFromString(String name, String code) {
        super( URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),Kind.SOURCE);
        this.code = code;
    }

    @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}

and I have made Instances of this class, added it to an ArrayList, then Got the

ToolProvider.SystemJavaCompiler();

and added compilation options. and then Compiled

Iterable<? extends JavaFileObject> fileObjects = jsfsList;

JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
if (jc == null) throw new Exception("Compiler unavailable");

List<String> options = new ArrayList<>();
options.add("-d");
options.add(Config.getProperty("DESTINATION_PATH"));
options.add("-classpath");

URLClassLoader urlClassLoader = (URLClassLoader)Thread.currentThread().getContextClassLoader();
StringBuilder sb = new StringBuilder();
for (URL url : urlClassLoader.getURLs()) {
    sb.append(url.getFile()).append(File.pathSeparator);
}
sb.append(PiranhaConfig.getProperty("DESTINATION_PATH"));
options.add(sb.toString());

StringWriter output = new StringWriter();
boolean success = jc.getTask(output, null, null, options, null, fileObjects).call();
if (success) {
    LOG.info("Class [" + compiledClasses + "] has been successfully compiled");
} else {
    throw new Exception("Compilation failed :" + output);
}

I have tested this with 3 classes that have circular dependency. it gives the error that it cannot find the symbol of a reference. it seems that unlike javac, this Compiler looks at each item in the list individually and tries to compile each alone.

how to achieve the same result as Javac using this compiler?? Someone please point me in the right direction :) Thanks.

Bhanuka Yd
  • 646
  • 8
  • 25
  • also note that these 3 test classes are form 3 different packages :) – Bhanuka Yd Apr 14 '16 at 08:50
  • Circular interdependencies between packages aren't such a good idea in the first place... – Andy Turner Apr 14 '16 at 08:56
  • Although I think that the question is reasonably interesting, I have voted to close it: "Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve)." Can you update your example so that a) we can compile it, b) it reproduces the issue, c) it contains specific errors you found. – Andy Turner Apr 14 '16 at 09:04

2 Answers2

0

The following code runs successfully for me (pretty much the same as OP's code, just replaces undefined symbols with reasonable alternatives):

import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

class CircularDeps {
  public static void main(String[] args) throws Exception {
    Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(
        new JavaSourceFromString(
            "A",
            "package packageA; public class A { packageB.B b; }"),
        new JavaSourceFromString(
            "B",
            "package packageB; public class B { packageC.C c; }"),
        new JavaSourceFromString(
            "C",
            "package packageC; public class C { packageA.A a; }")
        );

    JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
    if (jc == null) throw new Exception("Compiler unavailable");

    List<String> options = new ArrayList<>();
    options.add("-d");
    options.add(args[0]);
    options.add("-classpath");

    URLClassLoader urlClassLoader = (URLClassLoader)Thread.currentThread().getContextClassLoader();
    StringBuilder sb = new StringBuilder();
    for (URL url : urlClassLoader.getURLs()) {
          sb.append(url.getFile()).append(File.pathSeparator);
    }
    sb.append("output");
    options.add(sb.toString());

    StringWriter output = new StringWriter();
    boolean success = jc.getTask(output, null, null, options, null, fileObjects).call();
    if (success) {
          System.out.println("Classes has been successfully compiled");
    } else {
          throw new Exception("Compilation failed :" + output);
    }
  }
}

Output:

Classes has been successfully compiled
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

I have found a solution for the problem,

in the compilation process I have made a ArrayList to hold the SimpleJavaFileObject (classes to compile) instances, instead I have added a LinkedList.

with the link list it works fine :)

Bhanuka Yd
  • 646
  • 8
  • 25