Read a .java file from another class and call its method - java

Is it possible that to read a .java file as a file (using file path)from another class and call its method in this class ?
Lets takes a java class as
public Mylogic {
public static void test()
{
 //some logic
}
}
Is there a way another java class can read Mylogic.java file as a file and execute test() method
?
Why I want this?
Once source code goes into application server , then if I have to add another class , I have to wait for complete deployment which takes time.If I am able to do this, I can keep a utility class ready in source code to read .java file from dir and execute it without any deployment, thus saving time.
This is for higher environment (production mode) so no exploded mode.

It is possible, if you have JDK instead of JRE when running the code, using javax.tools.JavaCompiler and a customized class loader:
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class LoadAndRun extends ClassLoader {
public Class findClass(String name) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
InputStream in = new FileInputStream("./" + name + ".class");
byte [] buff = new byte[4096];
while (true) {
int c = in.read(buff);
if (c == -1) break;
out.write(buff, 0, c);
}
return defineClass(name, out.toByteArray(), 0, out.toByteArray().length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void main(String [] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
// Try to get the system compiler
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
System.err.println("System java compiler not found.");
return;
}
// Prepare the java file to be compiled
String classname = "LoadAndRun" + System.currentTimeMillis();
File javaFile = new File(classname + ".java");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(javaFile)));
writer.write("public class " + classname + "{\n");
writer.write("public static void test() {System.out.println(\"this is the test class:\"+"+classname+".class);}");
writer.write("}\n");
writer.close();
// Compile it!
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
compiler.getTask(null, fileManager, null, null, null, fileManager.getJavaFileObjects(javaFile)).call();
// Load and invoke static method
ClassLoader classLoader = new LoadAndRun();
Class cls = classLoader.loadClass(classname);
Method m = cls.getMethod("test");
m.invoke(null);
// Clean up
m = null;
cls = null;
classLoader = null;
System.gc();
javaFile.delete();
File classFile = new File(classname + ".class");
classFile.delete();
}
}

Related

Create a java class dynamically and compile and instantiate at run time

I have a String that I need to convert to java class, compile and create an instance at run time:
Suppose my String is:
String s = " public class Test {
public Double add(Double x, Double y){
return (x+y);
}
}"
How can I convert it to a class Test.class, instantiate it and call the method add(Double x, Double y) at run time?
I read about Byte Buddy , but the examples I see has a class already defined. In a situation like the above, could anyone give an example how can I use ByteBuddy or any other libraries through which I can achieve this?
Any inputs or suggestions on how to convert this String to compilable and instantiable java class would be helpful.
Can we use something like this :
package com.demo;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
public class StringToClass
{
public static void main(String[] args)
{
String s = "import com.demo.FormulaAPI; public class FormulaExecutor" +
" { public Double formula1(FormulaAPI apiReference)" +
" { System.out.println(apiReference.evaluate(\"10.10\")); return apiReference.evaluate(\"10.10\"); } }";
try
{
dynamicClass(s, "FormulaExecutor");
} catch (IOException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e)
{
e.printStackTrace();
}
}
static void dynamicClass(String sourceCode, String className) throws IOException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException
{
File parent = new File(System.getProperty("user.dir"));
File sourceFile = new File(parent, className + ".java");
sourceFile.deleteOnExit();
FileWriter writer = new FileWriter(sourceFile);
writer.write(sourceCode);
writer.close();
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
File parentDir = sourceFile.getParentFile();
standardJavaFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(parentDir));
Iterable<? extends JavaFileObject> compilationUnits = standardJavaFileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
javaCompiler.getTask(null, standardJavaFileManager, null, null, null, compilationUnits).call();
standardJavaFileManager.close();
URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] {parentDir.toURI().toURL()});
Class<?> dynamicClass = urlClassLoader.loadClass(className);
Method formula1 = dynamicClass.getDeclaredMethod("formula1", FormulaAPI.class);
formula1.invoke(dynamicClass.newInstance(), new Object[] {new FormulaAPI()});
}
}
package com.demo;
public class FormulaAPI
{
public Double evaluate(String str)
{
return Double.valueOf(str);
}
}
For Now, Method name is hardcoded
Method addMethod = dynamicClass.getDeclaredMethod("add", Double.class, Double.class);
We can even generate it at runTime using reflection
We can import the class in the source code.
Byte Buddy works on the byte code level and does not process source code. You can use Javassist for this purpose which offers limited processing of source code as it ships its own compiler.
Alternatively, use the Java compiler API as suggested.

Update/Edit property file

I am testing a library (jar) that is using property (mytest.properties). They way the library (jar) loads the property is by doing
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("mytest.properties");
So what I want to test is what happens when the property file exist and when it does not exit. In order to test this I need to edit the property file once the JVM is started. I have tried doing that and does not work. Bellow is the code I tried to edit the property file but this always returns empty string.
Content of main_mytest.properties is:
a=hello world
b=hello java
Content of mytest.properties and empty.txt is empty.
""
My Class is:
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class MyPropertyFiles {
final static String resourcesPath = "./mytestproj/src/main/resources";
public static void main(String [] args) throws IOException {
Path source = Paths.get(resourcesPath + "/main_mytest.properties");
Path destination = Paths.get(resourcesPath + "/mytest.properties");
Path empty = Paths.get(resourcesPath + "/empty.txt");
try
{
Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("mytest.properties");
StringWriter writer = new StringWriter();
IOUtils.copy(input, writer, "utf-8");
String theString = writer.toString();
System.out.println("!!!!!!!!!!!!!!!! The String: \n" + theString);
}
catch (Exception e)
{
e.printStackTrace();
}
finally {
Files.copy(empty, destination, StandardCopyOption.REPLACE_EXISTING);
}
}
}
After doing some digging I don't think reloading the files in the ClassLoader after the JVM has started is allowed.

How to refer relative paths for code outside pom project?

I have a very different situation to deal with. Something never seen before.
I have a codebase which is not a maven based project. It basically is set of Pig Script that are executed on Hadoop Cluster.
Now there is requirement to test these scripts using PigUnit, so I created a maven based project with all dependencies needed for the project.
Visually it looks like
user_mapper/
src/main/
user.pig
other.pig
test/
pom.xml
src/java/
/UserTest.java
/OtherTest.java
As you could see, test is a maven based project in itself.
What I need
In UserTest.java I want to refer to relative path of user.pig
How can I provide a relative path in UserTest.java?
Try the following code (internally uses commons-io jar)
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FileReader {
Logger logger = Logger.getLogger(FileReader.class.getName());
static String webAppPath;
private static final boolean IS_WINDOWS = System.getProperty( "os.name" ).contains( "indow" );
private InputStream inputStream;
private static FileReader fileReader;
public String getAbsolutePath(Class appClass, String relativePath) {
try {
String parentPath = "";
if (StringUtils.isNotBlank(webAppPath)) {
parentPath = webAppPath;
} else {
parentPath = appClass.getProtectionDomain().getCodeSource().getLocation().getPath();
}
String osAppropriatePath = IS_WINDOWS ? parentPath.substring(1) : parentPath;
String absolutePath = osAppropriatePath + relativePath;
File file = new File(absolutePath);
if (!file.exists()) {
FileUtils.writeStringToFile(file, IOUtils.toString(readFile(relativePath), "UTF-8"));
}
return absolutePath;
} catch (IOException ioe) {
logger.log(Level.SEVERE, null, ioe);
return relativePath;
}
}
public void closeFileReader() {
synchronized (this) {
try {
inputStream.close();
} catch (IOException ex) {
Logger.getLogger(FileReader.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private FileReader() {
}
public static FileReader getInstance() {
return new FileReader();
}
public static String getWebAppPath() {
return webAppPath;
}
public static void setWebAppPath(String webAppPath) {
FileReader.webAppPath = webAppPath;
}
}
And call the class to get the relative path as follows
FileReader.getInstance().getAbsolutePath(user.pig, "user.pig");
I solved this issue by using java.io.File as
final String filePath = new File("../src/user.pig").getAbsolutePath();

How to compile and run inside the java program

I want to open a Java GUI using another Java program. It successfully compiled, but it can't be run. It says "cannot load main class". Here is my code:
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class Test {
public static void main(String[] args) {
try {
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
jc.run(System.in, System.out, System.err, "src/HelloWorld.java");
// I think this line has the error
Runtime.getRuntime().exec("cmd.exe /c start java src/HelloWorld");
} catch (Exception e) {
e.printStackTrace();
}
}
}
HelloWorld class:
import javax.swing.JFrame;
public class HelloWorld {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(100, 500);
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
There are any number of possible places your code might fail, for example, it could fail to compile for some reason; java may not be in the OS's search path; the java command may fail, as the src/HelloWorld parameter looks wrong.
I believe your command should look more like java -cp ./src HelloWorld or it should be executed from within the context of the src directory...
You really need to make every effort to diagnose potential issues, such as the DiagnosticCollector for the JavaCompiler and reading the processes InputStream and ErrorStream
Based on your HelloWorld.java and this previous answer I was able generate this example, which can successfully compile and run your class...
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
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 TestCompile {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder(64);
try {
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
// >>>> Compiler Stage
// 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<>();
optionList.add("-classpath");
optionList.add(System.getProperty("java.class.path"));
Iterable<? extends JavaFileObject> compilationUnit
= fileManager.getJavaFileObjectsFromFiles(Arrays.asList(new File("src/HelloWorld.java")));
JavaCompiler.CompilationTask task = compiler.getTask(
null,
fileManager,
diagnostics,
optionList,
null,
compilationUnit);
if (task.call()) {
// <<<< Compiler Stage
// >>>> Run stage...
ProcessBuilder pb = new ProcessBuilder("java", "-cp", "./src", "HelloWorld");
pb.redirectError();
Process p = pb.start();
InputStreamConsumer.consume(p.getInputStream());
p.waitFor();
// <<<< Run Stage
} else {
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.out.format("Error on line %d in %s%n",
diagnostic.getLineNumber(),
diagnostic.getSource().toUri());
}
}
} catch (InterruptedException ex) {
Logger.getLogger(TestCompile.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
public static class InputStreamConsumer implements Runnable {
private InputStream is;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
public InputStream getInputStream() {
return is;
}
public static void consume(InputStream is) {
InputStreamConsumer consumer = new InputStreamConsumer(is);
Thread t = new Thread(consumer);
t.start();
}
#Override
public void run() {
InputStream is = getInputStream();
int in = -1;
try {
while ((in = is.read()) != -1) {
System.out.print((char)in);
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
}
As an aternative to using ProcessBuilder pb = new ProcessBuilder("java", "-cp", "./src", "HelloWorld");, you could actually use...
ProcessBuilder pb = new ProcessBuilder("java", "HelloWorld");
pb.directory(new File("src"));
pb.redirectError();
Process p = pb.start();
Which will launch the java process in within the context of the src directory

Compile and execute java source code without knowing where the Main method is?

I am facing the following problem:
I will receive one or more .java files. The idea is to automate the compile and execute process.
I have not written nor viewed .java source files i receive.
There may be 1 file or multiple files in multiple directories.
All of this is done under linux ( Debian / CentOS ).
Here is an example case:
2 files are received:
SomeFile.java and SomeOtherFile.Java ( This one has the static public void main(String args[]){} method but i do NOT know that !)
A process picks up the files and compiles them in this way:
javac -encoding UTF-8 -sourcepath . -d . *.java
So my problem is: I do not know which package(if any) contains the Main method so i do not know what do execute ?
java packageName.SomeOtherFile
Plenty of ways:
Get a java source code parser, parse the source code, find the method
Compile everything, go over the resulting *.class files using reflection, find the method.
For a small enough number of files, just try them all
I have written something like this before:
package vonbulow.nicki;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
*
* #author Nicki
*/
public class AppLoader extends ClassLoader {
private static final String userdir = System.getenv("USERPROFILE");
private static final AppLoader instance = new AppLoader();
private static HashMap<String, Class> loaded = new HashMap<String, Class>();
public static void loadapp(final String name) {
if(loaded.containsKey(name)) {
Thread d = new Thread(new Runnable(){
public void run(){
try {
Class c = loaded.get(name);
Method m = c.getDeclaredMethod("main", String[].class);
m.invoke(null, (Object[])new String[]{null});
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
}
}
});
d.start();
return;
}
File ud = new File(userdir+"\\nvbapp");
ud.mkdir();
File[] fa = ud.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".appn");
}
});
for(final File f:fa) {
if(f.getName().split("\\.")[0].equalsIgnoreCase(name)) {
Thread t = new Thread(new Runnable() {
public void run() {
runapp(f, name);
}
});
t.start();
}
}
}
private static void runapp(File f, String nam) {
List<Class> classes = new ArrayList<Class>();
ZipFile jf;
String name = "";
try {
jf = new ZipFile(f);
Enumeration<? extends ZipEntry> eze = jf.entries();
while(eze.hasMoreElements()){
ZipEntry ze = eze.nextElement();
if(ze.getName().endsWith(".class")&&!ze.isDirectory()){
InputStream fis = jf.getInputStream(ze);
byte[] bytes = new byte[(int)ze.getSize()];
fis.read(bytes);
classes.add(instance.defineClass(getClassName(bytes), bytes, 0, bytes.length));
}
if(ze.getName().equalsIgnoreCase("META-INF/MANIFEST.MF")) {
Manifest manifest = new Manifest(jf.getInputStream(ze));
name = manifest.getMainAttributes().getValue("Main-Class");
}
}
Iterator<Class> classit = classes.iterator();
while(classit.hasNext()) {
Class c = classit.next();
if(c.getName().equals(name)) {
try {
loaded.put(nam, c);
Method m = c.getDeclaredMethod("main", String[].class);
m.invoke(null, (Object[]) new String[]{null});
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
ex.printStackTrace();
}
}
}
} catch (IOException ex) {
Logger.getLogger(AppLoader.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static String getClassName(byte[] is) {
try {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(is));
dis.readLong(); // skip header and class version
int cpcnt = (dis.readShort()&0xffff)-1;
int[] classes = new int[cpcnt];
String[] strings = new String[cpcnt];
for(int i=0; i<cpcnt; i++) {
int t = dis.read();
if(t==7) classes[i] = dis.readShort()&0xffff;
else if(t==1) strings[i] = dis.readUTF();
else if(t==5 || t==6) { dis.readLong(); i++; }
else if(t==8) dis.readShort();
else dis.readInt();
}
dis.readShort(); // skip access flags
return strings[classes[(dis.readShort()&0xffff)-1]-1].replace("/", ".");
} catch (IOException ex) {
return null;
}
}
}
I have not edited it; you will need to edit it so it loads your own classes.
You also need to compile the files first with the JavaCompiler class. This also assumes that the classes are in the zip file.

Categories