Loading Remote Groovy Script - java

I'm loading a Groovy script/class from a remote server, creating a new instance, and invoking a method, as shown below:
String[] scriptUrls = { "http://10.74.192.186/groovy/Test.groovy" };
GroovyScriptEngine gse = new GroovyScriptEngine(scriptUrls);
Class groovyClass = gse.loadScriptByName("Test.groovy");
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
Object[] callArgs = {};
System.out.println(groovyObject.invokeMethod("getTest", callArgs));
How is the Groovy file compiled to bytecode when pulled off a server like this? Do I only need a JRE to run this?
I'm just a little confused how this works internally.
For reference, here is the Groovy file:
class Test {
String test = "test"
}
Thanks.

The Groovy file is compiled with the Groovy compiler, no JDK is needed (Groovy compiles directly to bytecode).

Related

How to use Java Compile API to compile recursively? [duplicate]

I use the class javax.tools.JavaCompiler (jdk6) to compile a source file, but the source file depends on some jar file. How to set the classpath of the javax.tools.JavaCompiler?
The javax.tools.JavaCompiler#getTask() method takes an options parameter that allows to set compiler options. The following message describes an easy way to set them in order to access the calling program's classpath:
You need to configure the standard
java file manager to know about the
jar files(s) - you use the compiler
options argument to do that.
By default the java compiler object
only seems to know about the default
locations for bootclasspath, extdirs
and endorseddirs directories in terms
of its classpath.
You need to add the calling program's
current classpath to the java compiler
instance's which gets passed on the
the standard file manager, which will
then find classes in the jar files.
Here's how I do it in the compiler
wrapper I wrote
List<String> optionList = new ArrayList<String>();
// set compiler's classpath to be same as the runtime's
optionList.addAll(Arrays.asList("-classpath",System.getProperty("java.class.path")));
// any other options you want
optionList.addAll(Arrays.asList(options));
JavaCompiler.CompilationTask task = compiler.getTask(out,jfm,diagnostics,optionList,null,jfos);
All you'll need then is to get the proper classpath set when running the calling program.
The same problem occurred to me recently, finally I found two workarounds. You can set the class path either by invoke StandardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, "YOUR_CLASS_PATH") or Compiler.getTask(ARG_0, ARG_1, ARG_2, CLASS_PATH_OPTIONS, just as the first answer posted here says.
I needed something simpler than the examples above.
The following is a self-contained example of using the built-in Java compiler, and setting the classpath for the compiler to use.
It is equivalent to creating a source file called HelloPrinter.java and then compiling it as follows:
javac -classpath C:\Users\dab\Testing\a.jar;c:\path\etc org\abc\another\HelloPrinter.java
Note how the classpath can be set using a String[] of options. This should be familiar if you're already used to running javac on the command line (as above).
This code is compatible with Java 6. You will need a JDK, not a JRE, for this to run. This example doesn't actually use the classpath. It all does is print "Hello". You can add an import statement to the generated source and call a method in an external Jar file to test this properly.
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class JavaCompilerExample {
public static void main(String[] args) throws Exception {
String className = "HelloPrinter";
String directoryName = "org/abc/another";
new File(directoryName).mkdirs();
FileOutputStream fos = new FileOutputStream(directoryName+"/"+className+".java");
PrintStream ps = new PrintStream(fos);
ps.println(
"package "+directoryName.replace("/", ".") + " ; "
+ "public class " +className +
"{ public static void main(String[] args){System.out.println(\"Hello\");} }");
ps.close();
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
String javacOpts[] = {"-classpath",
"C:\\Users\\dab\\Testing\\a.jar;c:\\path\\etc;",
directoryName+"/"+className + ".java"};
if ( javac.run(null, null, null, javacOpts)!=0 ) {
System.err.println("Error");
System.exit(1);
}
}
}

Compiling a program inside of another program [to a jar file?] [duplicate]

I have the class name stored in a property file. I know that the classes store will implement IDynamicLoad. How do I instantiate the class dynamically?
Right now I have
Properties foo = new Properties();
foo.load(new FileInputStream(new File("ClassName.properties")));
String class_name = foo.getProperty("class","DefaultClass");
//IDynamicLoad newClass = Class.forName(class_name).newInstance();
Does the newInstance only load compiled .class files? How do I load a Java Class that is not compiled?
How do I load a Java Class that is not compiled?
You need to compile it first. This can be done programmatically with the javax.tools API. This only requires the JDK being installed at the local machine on top of JRE.
Here's a basic kickoff example (leaving obvious exception handling aside):
// Prepare source somehow.
String source = "package test; public class Test { static { System.out.println(\"hello\"); } public Test() { System.out.println(\"world\"); } }";
// Save source in .java file.
File root = new File("/java"); // On Windows running on C:\, this is C:\java.
File sourceFile = new File(root, "test/Test.java");
sourceFile.getParentFile().mkdirs();
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("test.Test", true, classLoader); // Should print "hello".
Object instance = cls.newInstance(); // Should print "world".
System.out.println(instance); // Should print "test.Test#hashcode".
Which yields like
hello
world
test.Test#ab853b
Further use would be more easy if those classes implements a certain interface which is already in the classpath.
SomeInterface instance = (SomeInterface) cls.newInstance();
Otherwise you need to involve the Reflection API to access and invoke the (unknown) methods/fields.
That said and unrelated to the actual problem:
properties.load(new FileInputStream(new File("ClassName.properties")));
Letting java.io.File rely on current working directory is recipe for portability trouble. Don't do that. Put that file in classpath and use ClassLoader#getResourceAsStream() with a classpath-relative path.
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties"));
In the same vein as BalusC's answer, but a bit more automatic wrapper is here in this piece of code from my kilim distribution.
https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java
It takes a list of strings containing Java source, extracts the package and public class/interface names and creates the corresponding directory/file hierarchy in a tmp directory. It then runs the java compiler on it, and returns a list of name,classfile pairs (the ClassInfo structure).
Help yourself to the code. It is MIT licensed.
Your commented code is correct if you know that the class has a public no-arg constructor. You just have to cast the result, as the compiler can't know that the class will in fact implement IDynamicLoad. So:
IDynamicLoad newClass = (IDynamicLoad) Class.forName(class_name).newInstance();
Of course the class has to be compiled and on the classpath for that to work.
If you are looking to dynamically compile a class from source code, that is a whole other kettle of fish.

ClassLoader always returns null when called from within a jar

I ran into library loading problems after creating a jar from my code via maven. I use intelliJ idea on Ubuntu. I broke the problem down to this situation:
Calling the following code from within idea it prints the path correctly.
package com.myproject;
public class Starter {
public static void main(String[] args) {
File classpathRoot = new File(Starter.class.getResource("/").getPath());
System.out.println(classpathRoot.getPath());
}
}
Output is:
/home/ted/java/myproject/target/classes
When I called mvn install and try to run it from command line using the following command I'm getting a NullPointerException since class.getResource() returns null:
cd /home/ted/java/myproject/target/
java -cp myproject-0.1-SNAPSHOT.jar com.myproject.Starter
same for calling:
cd /home/ted/java/myproject/target/
java -Djava.library.path=. -cp myproject-0.1-SNAPSHOT.jar com.myproject.Starter
It doesn't matter if I use class.getClassLoader().getRessource("") instead. Same problem when accessing single files inside of the target directory instead via class.getClassLoader().getRessource("file.txt").
I want to use this way to load native files in the same directory (not from inside the jar). What's wrong with my approach?
The classpath loading mechanism in the JVM is highly extensible, so it's often hard to guarantee a single method that would work in all cases. e.g. What works in your IDE may not work when running in a container because your IDE and your container probably have highly specialized class loaders with different requirements.
You could take a two tiered approach. If the method above fails, you could get the classpath from the system properties, and scan it for the jar file you're interested in and then extract the directory from that entry.
e.g.
public static void main(String[] args) {
File f = findJarLocation("jaxb-impl.jar");
System.out.println(f);
}
public static File findJarLocation(String entryName) {
String pathSep = System.getProperty("path.separator");
String[] pathEntries = System.getProperty("java.class.path").split(pathSep);
for(String entry : pathEntries) {
File f = new File(entry);
if(f.getName().equals(entryName)) {
return f.getParentFile();
}
}
return null;
}

Building Java file at runtime & running JUnit test on that class file

I am trying to build a Dynamic web project where user can practice Java code.
I got success on writing the code written by user in a .java file, compile the code & get error messages using Java Compile API.
Now, I need to run JUnit 1.4 Compatible test on that code.
I researched for it, and found something like parameterized junit testing. But my view on how should it be done isn't still clear.
UPDATE
This (http://codingbat.com/prob/p171896) is the exact thing what I'm trying to implement.
I solved this problem with the following steps:
Compile the JUnit TestClass and the ClassToTest
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compilerResult = compiler.run(null, null, null,"/path/to/TestClass.java", "/path/to/ClassToTest.java");
System.out.println("Compiler result code: " + compilerResult);
Load the compiled classes into the classloader
File dir = new File("/path/to/class/files/");
URL url = dir.toURI().toURL();
URL[] urls = {url};
ClassLoader classLoader = new URLClassLoader(urls);
Run the tests (make sure that you add junit as a dependency of your project)
Class<?> junitTest = Class.forName("TestClass", true, classLoader);
Result result = junit.run(junitTest);
Edit: If you're not needing the JUnit tests to be dynamic you can skip the compilation of those tests and add them here: junit.run(JunitTest.class)

How to set classpath when I use javax.tools.JavaCompiler compile the source?

I use the class javax.tools.JavaCompiler (jdk6) to compile a source file, but the source file depends on some jar file. How to set the classpath of the javax.tools.JavaCompiler?
The javax.tools.JavaCompiler#getTask() method takes an options parameter that allows to set compiler options. The following message describes an easy way to set them in order to access the calling program's classpath:
You need to configure the standard
java file manager to know about the
jar files(s) - you use the compiler
options argument to do that.
By default the java compiler object
only seems to know about the default
locations for bootclasspath, extdirs
and endorseddirs directories in terms
of its classpath.
You need to add the calling program's
current classpath to the java compiler
instance's which gets passed on the
the standard file manager, which will
then find classes in the jar files.
Here's how I do it in the compiler
wrapper I wrote
List<String> optionList = new ArrayList<String>();
// set compiler's classpath to be same as the runtime's
optionList.addAll(Arrays.asList("-classpath",System.getProperty("java.class.path")));
// any other options you want
optionList.addAll(Arrays.asList(options));
JavaCompiler.CompilationTask task = compiler.getTask(out,jfm,diagnostics,optionList,null,jfos);
All you'll need then is to get the proper classpath set when running the calling program.
The same problem occurred to me recently, finally I found two workarounds. You can set the class path either by invoke StandardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, "YOUR_CLASS_PATH") or Compiler.getTask(ARG_0, ARG_1, ARG_2, CLASS_PATH_OPTIONS, just as the first answer posted here says.
I needed something simpler than the examples above.
The following is a self-contained example of using the built-in Java compiler, and setting the classpath for the compiler to use.
It is equivalent to creating a source file called HelloPrinter.java and then compiling it as follows:
javac -classpath C:\Users\dab\Testing\a.jar;c:\path\etc org\abc\another\HelloPrinter.java
Note how the classpath can be set using a String[] of options. This should be familiar if you're already used to running javac on the command line (as above).
This code is compatible with Java 6. You will need a JDK, not a JRE, for this to run. This example doesn't actually use the classpath. It all does is print "Hello". You can add an import statement to the generated source and call a method in an external Jar file to test this properly.
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class JavaCompilerExample {
public static void main(String[] args) throws Exception {
String className = "HelloPrinter";
String directoryName = "org/abc/another";
new File(directoryName).mkdirs();
FileOutputStream fos = new FileOutputStream(directoryName+"/"+className+".java");
PrintStream ps = new PrintStream(fos);
ps.println(
"package "+directoryName.replace("/", ".") + " ; "
+ "public class " +className +
"{ public static void main(String[] args){System.out.println(\"Hello\");} }");
ps.close();
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
String javacOpts[] = {"-classpath",
"C:\\Users\\dab\\Testing\\a.jar;c:\\path\\etc;",
directoryName+"/"+className + ".java"};
if ( javac.run(null, null, null, javacOpts)!=0 ) {
System.err.println("Error");
System.exit(1);
}
}
}

Categories