I need a way to run a java method for ex. createModule("Login")
and as an output to have:
New folder named mod_login
Inside on mod_login java classes files created from a template
If the template is
class Name extends Blah implement Blah {
private createdInt;
private int getCreatedInt() {
return createdInt;
}
}
In return I want to get a dynamically created class:
class Login extends Blah implement Blah {
private loginInt;
private int getLoginInt() {
return loginInt;
}
}
Tried to look into groovy to do it, but could not find anything usefull.
P.S. it shouldn't happen on runtime, it's a more like a helper to instantiate these modules with just 1 button, instead of typing them
Working example which will help you.
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
public class HelloWorld {
public static void main(String[] args) throws Exception {
// create an empty source file
File sourceFile = File.createTempFile("Hello", ".java");
sourceFile.deleteOnExit();
// generate the source code, using the source filename as the class name
String classname = sourceFile.getName().split("\\.")[0];
String sourceCode = "public class " + classname + "{ public void hello() { System.out.print(\"Hello world\");}}";
// write the source code into the source file
FileWriter writer = new FileWriter(sourceFile);
writer.write(sourceCode);
writer.close();
// compile the source file
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
File parentDirectory = sourceFile.getParentFile();
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(parentDirectory));
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
fileManager.close();
// load the compiled class
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentDirectory.toURI().toURL() });
Class<?> helloClass = classLoader.loadClass(classname);
// call a method on the loaded class
Method helloMethod = helloClass.getDeclaredMethod("hello");
helloMethod.invoke(helloClass.newInstance());
}
}
You just need 2 variables defined in a method which creates files dynamically.
className
&
propertyName
Now use these to create a file with .java extension and write the text from your template as is. For className and propertyName generation logic use the variables that you have above.
If you want to create more than one such files then take className & propertyName in a list and run a forloop.
Related
My objective is to look at some lines of codes of an external file and count the number of functions of a class are called then.
For example, if I have the following code:
import java.io.BufferedReader;
import whatever.MyClass;
import java.util.ArrayList;
...
...
public void example(){
InputStreamReader isr = new InputStreamReader (whatever);
MyClass object = new MyClass();
someArrayList.add(whatever2)
someArrayList.add(whatever3)
}
In this case, BufferedReader and MyClass functions were called once, and ArrayList functions were called twice.
My solution for that is get a list of all methods inside the used classes and try to match with some string of my code.
For classes created in my project, I can do the following:
jar -tf jarPath
which returns me the list of classes inside a JAR . And doing:
javap -cp jarPath className
I can get a list of all methods inside a JAR whit a specific class name. However, what can I do to get a external methods names, like add(...) of an "external" class java.util.ArrayList?
I can't access the .jar file of java.util.ArrayList correct? Anyone have another suggestion to reach the objective?
The compiler doesn't put the imports into the object file. It throws them away. Import is just a shorthand to the compiler.(Imports are a compile-time feature ).
first step :
use Qdox https://github.com/paul-hammant/qdox to get all the imports in a class :
String fileFullPath = "Your\\java\\ file \\full\\path";
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSource(new FileReader( fileFullPath ));
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
System.out.println(imp);
}
second step :
inspire from that code , loop through your imports (String array) and apply the same code and you will get the methods .
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Tes {
public static void main(String[] args) {
Class c;
try {
c = Class.forName("java.util.ArrayList");
Arrays.stream(getAccessibleMethods(c)).
forEach(System.out::println);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
result.add(method);
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
}
Output :
public void java.util.ArrayList.add(int,java.lang.Object)
public boolean java.util.ArrayList.add(java.lang.Object)
public boolean java.util.ArrayList.remove(java.lang.Object)
public java.lang.Object java.util.ArrayList.remove(int)
public java.lang.Object java.util.ArrayList.get(int)
public java.lang.Object java.util.ArrayList.clone()
public int java.util.ArrayList.indexOf(java.lang.Object)
public void java.util.ArrayList.clear()
.
.
.
All the code - one class :
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaSource;
public class Tester {
public static void main(String[] args) {
// put your .java file path
// CyclicB is a class within another project in my pc
String fileFullPath =
"C:\\Users\\OUSSEMA\\Desktop\\dev\\OCP_Preparation\\src\\w\\CyclicB.java";
JavaDocBuilder builder = new JavaDocBuilder();
try {
builder.addSource(new FileReader( fileFullPath ));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
Class c;
try {
c = Class.forName(imp);
Arrays.stream(getAccessibleMethods(c)).
forEach(System.out::println);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
result.add(method);
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
}
Output all the methods within the classes imported in the file CyclicB.java :
private void java.lang.Throwable.printStackTrace(java.lang.Throwable$PrintStreamOrWriter)
public void java.lang.Throwable.printStackTrace(java.io.PrintStream)
public void java.lang.Throwable.printStackTrace()
public void java.lang.Throwable.printStackTrace(java.io.PrintWriter)
public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
.
.
.
You may look into OpenJDK project that has a Java compiler. Learn to build the modified versions. Investigate the syntax analysis layer of this compiler and find where the method calls are handled. Put the logging into these locations and now you only need to build your java file with the modified compiler to get the information about the calls.
The build is complex, but you will likely only need a careful editing in a few files. It is not exactly very low hanging fruit but I think it should be possible to discover these files and make changes in them, and still may be a simpler/cleaner approach than to implement the own Java syntax parser (also doable with JavaCC).
If you also need to track calls from the external libraries, build them with the modified compiler as well and you will have the needed records.
GNU Classpath is another open source project where you can do the similar thing, and it may be easier to build. However, unlike OpenJDK, GNU Classpath java system library is not complete.
This approach may not discover some methods called during reflection. But it would discover that reflection framework methods have been called. If it is a security - related project, the simplest would be to agree that reflection is not allowed. It is uncommon to use reflection in a normal Java application that is not a framework.
This question already has answers here:
How to provide an interface to JavaCompiler when compiling a source file dynamically?
(3 answers)
Closed 5 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
(This question is similar to many questions I have seen but most are not specific enough for what I am doing)
Background:
The purpose of my program is to make it easy for people who use my program to make custom "plugins" so to speak, then compile and load them into the program for use (vs having an incomplete, slow parser implemented in my program). My program allows users to input code into a predefined class extending a compiled class packaged with my program. They input the code into text panes then my program copies the code into the methods being overridden. It then saves this as a .java file (nearly) ready for the compiler. The program runs javac (java compiler) with the saved .java file as its input.
My question is, how do I get it so that the client can (using my compiled program) save this java file (which extends my InterfaceExample) anywhere on their computer, have my program compile it (without saying "cannot find symbol: InterfaceExample") then load it and call the doSomething() method?
I keep seeing Q&A's using reflection or ClassLoader and one that almost described how to compile it, but none are detailed enough for me/I do not understand them completely.
Take a look at JavaCompiler
The following is based on the example given in the JavaDocs
This will save a File in the testcompile directory (based on the package name requirements) and the compile the File to a Java class...
package inlinecompiler;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class InlineCompiler {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder(64);
sb.append("package testcompile;\n");
sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {\n");
sb.append(" public void doStuff() {\n");
sb.append(" System.out.println(\"Hello world\");\n");
sb.append(" }\n");
sb.append("}\n");
File helloWorldJava = new File("testcompile/HelloWorld.java");
if (helloWorldJava.getParentFile().exists() || helloWorldJava.getParentFile().mkdirs()) {
try {
Writer writer = null;
try {
writer = new FileWriter(helloWorldJava);
writer.write(sb.toString());
writer.flush();
} finally {
try {
writer.close();
} catch (Exception e) {
}
}
/** Compilation Requirements *********************************************************************************************/
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
// This sets up the class path that the compiler will use.
// I've added the .jar file that contains the DoStuff interface within in it...
List<String> optionList = new ArrayList<String>();
optionList.add("-classpath");
optionList.add(System.getProperty("java.class.path") + File.pathSeparator + "dist/InlineCompiler.jar");
Iterable<? extends JavaFileObject> compilationUnit
= fileManager.getJavaFileObjectsFromFiles(Arrays.asList(helloWorldJava));
JavaCompiler.CompilationTask task = compiler.getTask(
null,
fileManager,
diagnostics,
optionList,
null,
compilationUnit);
/********************************************************************************************* Compilation Requirements **/
if (task.call()) {
/** Load and execute *************************************************************************************************/
System.out.println("Yipe");
// Create a new custom class loader, pointing to the directory that contains the compiled
// classes, this should point to the top of the package structure!
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./").toURI().toURL()});
// Load the class from the classloader by name....
Class<?> loadedClass = classLoader.loadClass("testcompile.HelloWorld");
// Create a new instance...
Object obj = loadedClass.newInstance();
// Santity check
if (obj instanceof DoStuff) {
// Cast to the DoStuff interface
DoStuff stuffToDo = (DoStuff)obj;
// Run it baby
stuffToDo.doStuff();
}
/************************************************************************************************* Load and execute **/
} else {
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.out.format("Error on line %d in %s%n",
diagnostic.getLineNumber(),
diagnostic.getSource().toUri());
}
}
fileManager.close();
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) {
exp.printStackTrace();
}
}
}
public static interface DoStuff {
public void doStuff();
}
}
Now updated to include suppling a classpath for the compiler and loading and execution of the compiled class!
I suggest using the Java Runtime Compiler library. You can give it a String in memory and it will compile and load the class into the current class loader (or one of your choice) and return the Class loaded. Nested classes are also loaded. Note: this works entirely in memory by default.
e.g.
// dynamically you can call
String className = "mypackage.MyClass";
String javaCode = "package mypackage;\n" +
"public class MyClass implements Runnable {\n" +
" public void run() {\n" +
" System.out.println(\"Hello World\");\n" +
" }\n" +
"}\n";
Class aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, javaCode);
Runnable runner = (Runnable) aClass.newInstance();
runner.run();
The code is as follows
what it does is it loads all the classes inside a jar file which I placed inside my home directory .
import java.io.File;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.net.URLClassLoader;
import java.net.URL;
import java.util.Enumeration;
import java.lang.ClassLoader;
public class Plugin extends ClassLoader {
public static void main(String[] args) throws Exception {
File file = new File(System.getProperty("user.home") + "/HelloWorld.jar");
URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()});
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry element = entries.nextElement();
if (element.getName().endsWith(".class")) {
try {
Class c = clazzLoader.loadClass(element.getName().replaceAll(".class", "").replaceAll("/", "."));
c.newInstance(); // this proves that class is loaded
} catch (Exception e) {
e.printStackTrace();
}
}
}
Class cls = Class.forName("HelloWorld");
cls.newInstance();
Plugin p = new Plugin();
p.checkIfLoaded();
}
public void checkIfLoaded() {
System.out.println("coming in");
if (findLoadedClass("HelloWorld") != null){
System.out.println("Yepee, HelloWorld class is loaded !");
}
}
}
My HelloWorld is as in https://github.com/HarishAtGitHub/doc/blob/master/makeExecutableJar/HelloWorld.java
and the jar is got using the instructions in my github account mentioned above .
c.newInstance() works .
How did I confirm ?
the static block got executed ...
but Class.forName("HelloWorld") throws ClassNotFoundException
also findLoadedClass("HelloWorld") is null ..
I cannot understand why this strange behaviour ?
Please guide ...
This is a classloader issue.
As per the Javadocs to Class.forName, you are looking up the class using the classloader of the current class. As your main class, this will be the JVM's bootstrap classloader (and will more or less just include the standard library plus anything you provided as a -cp command line argument). It is not going to delegate to the classloader that you instantiated as a local variable, and so will not return classes that that classloader could find.
If you were to specify the classloader explicitly, and call
Class.forName("HelloWorld", true, clazzloader)
then the classloader you just created will be searched instead and your class should be found.
Because Class.forName(String) uses currentClassLoader and you have load the class in different ClassLoader.
According with javadoc, invoking Class.forName(String) is equivalent to:
Class.forName(className, true, currentLoader)
I have a requirement where i have to dynamically load java program based on the input. All java class files are placed in folder : C://Users/me/workspace/File/bin/Encrypt/.
there are 3 class files: Class1.class, Class2.class, Class3.class in this folder
To pick them up in runtime i am using below code:
package First;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class Main
{
public static void main(String[] args) {
// Say Class.class is the input file to be picked up.
String abc = "Class1.class";
try {
File file = new File("C:\\Users\\me\\workspace\\File\\bin\\Encrypt");
//convert the file to URL format
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
//load this folder into Class loader
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(abc);
System.out.println("cls.getName() = " + cls.getName());
cls.encrypt();
}
catch ( ClassNotFoundException | MalformedURLException e) {
e.printStackTrace();
}
}
}
I am facing below issues here:
The above code shows the error: .NoClassDefFoundError: Class1??
have used all types of - / ,\ ,//,\ in the path.
How can i call a method sum() in Class1 file ??
since you have ...\bin\Encrypt is the folder where you classes are i would bet the package for your classes is Encrypt. in this case abc should be
String abc = "Enrcypt.Class1";
if the classes are in the default package which means they have no package declaration than use
String abc = "Class1";
note also that there is no need for the extension .class in the name of the class.
To call the a method of class loaded this way you should refer to java refelction api.
first get a Method object from Class cl using getMethod
than use that method object's method invoke to call the function. you'll need to provide an instance of the Class dynamically loaded if the the method to call is not static, if it is static just pass null.
I am using com.sun.tools.javac.Main.compile() function to compile java file at run time from my struts projects. But for some files they need some specific jars like axis2. I have the jars but how can i set them to classpath to compile the java file at runtime? I have tried with System.setProperty("java.class.path","jar dir"); but failed to compile.
The following code which uses com.sun.tools.javac.Main worked for me:
Apple.java
//This class is packaged in a jar named MyJavaCode.jar
import com.xyz.pqr.SomeJavaExamples;
public class Apple {
public static void main(String[] args) {
System.out.println("hello from Apple.main()");
}
}
AClass.java
import com.sun.tools.javac.Main;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class AClass {
public static void main(String[] args) {
try {
//Specify classpath using next to -cp
//This looks just like how we specify parameters for javac
String[] optionsAndSources = {
"-g", "-source", "1.5",
"-target", "1.5",
"-cp", ".:/home/JavaCode/MyJavaCode.jar",
"Apple.java"
};
PrintWriter out = new PrintWriter(new FileWriter("./out.txt"));
int status = Main.compile(optionsAndSources, out);
System.out.println("status: " + status);
System.out.println("complete: ");
}catch (Exception e) {}
}
}
Note: To compile this AClass.java, tools.jar needs to be in the classpath, which is not there by default, so you will have to specify it.
If you are using Java 1.6 then you should consider using javax.tools.JavaCompiler instead, its getTask() methods takes an argument options which can have the classpath.
For example:
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import javax.tools.JavaFileObject;
public final class AClass {
private static boolean compile(JavaFileObject... source ){
List<String> options = new ArrayList<String>();
// set compiler's classpath to be same as the runtime's
options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
//Add more options including classpath
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final JavaCompiler.CompilationTask task = compiler.getTask(/*default System.err*/ null,
/*std file manager*/ null,
/*std DiagnosticListener */ null,
/*compiler options*/ options,
/*no annotation*/ null,
Arrays.asList(source));
return task.call();
}
com.sun.tools.javac.Main is deprecated and undocumented too.