Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
code_parser=System.out.println("Complex Java code to be passed through parameter file".toUpperCase())
I want to pass code in standard talend jobs as a filter from parameter file through Context which can be changed later on as per business need. This Java String has to be converted into executable code in Talend.
I'll be honest, I am not sure why you would want to do this, but I was fascinated to see if I could. There is a big problem with compiling code that uses classes that do not exist. So this has to be done in such a way that class specifics (of the class you want to compile at runtime) are minimised.
The job I wrote to do this look like this.....
The tFixedFlowInput is where I am supplying the code for this example. The code is just passed in as a String. The code is being written to a file that I am calling myCode.java using the tFileOutputRaw. That's pretty simple enough. FYI the code that I am passing to the file looks like this.....
public class MyCode {
String data1;
String data2;
public MyCode(String data1, String data2){
this.data1 = data1;
this.data2 = data2;
}
public String toString(){
return data1 + " " + data2 + " how are you?";
}
}
It is added to the tFixedFlowInput as such (note the escaped quotes as quotes are used in Java to denote a String)....
"public class MyCode {String data1;String data2;public MyCode(String data1, String data2){ this.data1 = data1;this.data2 = data2;}public String toString(){return data1 + \" \" + data2 + \" how are you?\";}}"
The next step is to compile the file that we have just created and run it. FYI I found the basis of this code here ....
https://www.netjstech.com/2016/10/how-to-compile-java-program-at-runtime.html
I set up the tJavaFlex to have a single column output. This is called "class_data". This is printed using the tLogRow.
The code in the tJavaFlex can be seen below with notes....
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// Code to compile - Supplied to the tFileOutputRaw above
int result = compiler.run(null, null, null, "/Users/richardhall/Documents/MyCode.java");
// Path of the class
File classesDir = new File("/Users/richardhall/Documents");
// Load and instantiate compiled class.
URLClassLoader classLoader;
try {
// Loading the class
classLoader = URLClassLoader.newInstance(new URL[] { classesDir.toURI().toURL() });
Class<?> clazz = Class.forName("MyCode", true, classLoader);
//The following is a hack to get this to be of any use. What I am doing here is
//using the constructor of the class that I have created in the previous subjob to
//act as the only way of receiving variables.
Class[] cArg = new Class[2]; //Our constructor has 2 arguments
cArg[0] = String.class; //First argument is of *object* type String
cArg[1] = String.class; //Second argument is of *object* type String
String s1 = "Hello";
String s2 = "World";
//The following code instantiates the new class and immediately calls the toString() method
//This is because at compile time, this class we are building does not exist. So I have only access to
//methods that exist on the base Object class. The toString() method has been overridden to return
//the result.
row2.class_data = clazz.getDeclaredConstructor(cArg).newInstance(s1, s2).toString();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
With this code in place, you can modify the Java code used by the tFixedFlowInput and get a different result. To start with the code is initially concatenating the first constructor param String with the second, then adding " how are you?". If you have put together this job to try this out, the first Java class is below.....
"public class MyCode {String data1;String data2;public MyCode(String data1, String data2){ this.data1 = data1;this.data2 = data2;}public String toString(){return data1 + \" \" + data2 + \" how are you?\";}}"
......to test this with another Java class, just replace what is in the tFixedFlowInput with this....
"public class MyCode {String data1;String data2;public MyCode(String data1, String data2){ this.data1 = data1;this.data2 = data2;}public String toString(){return data1 + \" \" + data2 + \" did you see how this worked?\";}}"
As I said, I can't see a great many uses for this. It would be easier to alter the flow of code in a different way. But if you really want to do this and have a use case that maybe I am not thinking of, you can try something like this.
I'll be honest, I am not sure why you would want to do this, but I was fascinated to see if I could. There is a big problem with compiling code that uses classes that do not exist. So this has to be done in such a way that class specifics (of the class you want to compile at runtime) are minimised.
The job I wrote to do this look like this.....
The tFixedFlowInput is where I am supplying the code for this example. The code is just passed in as a String. The code is being written to a file that I am calling myCode.java using the tFileOutputRaw. That's pretty simple enough. FYI the code that I am passing to the file looks like this.....
public class MyCode {
String data1;
String data2;
public MyCode(String data1, String data2){
this.data1 = data1;
this.data2 = data2;
}
public String toString(){
return data1 + " " + data2 + " how are you?";
}
}
It is added to the tFixedFlowInput as such (note the escaped quotes as quotes are used in Java to denote a String)....
"public class MyCode {String data1;String data2;public MyCode(String data1, String data2){ this.data1 = data1;this.data2 = data2;}public String toString(){return data1 + \" \" + data2 + \" how are you?\";}}"
The next step is to compile the file that we have just created and run it. FYI I found the basis of this code here ....
https://www.netjstech.com/2016/10/how-to-compile-java-program-at-runtime.html
I set up the tJavaFlex to have a single column output. This is called "class_data". This is printed using the tLogRow.
The code in the tJavaFlex can be seen below with notes....
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// Code to compile - Supplied to the tFileOutputRaw above
int result = compiler.run(null, null, null, "/Users/richardhall/Documents/MyCode.java");
// Path of the class
File classesDir = new File("/Users/richardhall/Documents");
// Load and instantiate compiled class.
URLClassLoader classLoader;
try {
// Loading the class
classLoader = URLClassLoader.newInstance(new URL[] { classesDir.toURI().toURL() });
Class<?> clazz = Class.forName("MyCode", true, classLoader);
//The following is a hack to get this to be of any use. What I am doing here is
//using the constructor of the class that I have created in the previous subjob to
//act as the only way of receiving variables.
Class[] cArg = new Class[2]; //Our constructor has 2 arguments
cArg[0] = String.class; //First argument is of *object* type String
cArg[1] = String.class; //Second argument is of *object* type String
String s1 = "Hello";
String s2 = "World";
//The following code instantiates the new class and immediately calls the toString() method
//This is because at compile time, this class we are building does not exist. So I have only access to
//methods that exist on the base Object class. The toString() method has been overridden to return
//the result.
row2.class_data = clazz.getDeclaredConstructor(cArg).newInstance(s1, s2).toString();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
With this code in place, you can modify the Java code used by the tFixedFlowInput and get a different result. To start with the code is initially concatenating the first constructor param String with the second, then adding " how are you?". If you have put together this job to try this out, the first Java class is below.....
"public class MyCode {String data1;String data2;public MyCode(String data1, String data2){ this.data1 = data1;this.data2 = data2;}public String toString(){return data1 + \" \" + data2 + \" how are you?\";}}"
......to test this with another Java class, just replace what is in the tFixedFlowInput with this....
"public class MyCode {String data1;String data2;public MyCode(String data1, String data2){ this.data1 = data1;this.data2 = data2;}public String toString(){return data1 + \" \" + data2 + \" did you see how this worked?\";}}"
As I said, I can't see a great many uses for this. It would be easier to alter the flow of code in a different way. But if you really want to do this and have a use case that maybe I am not thinking of, you can try something like this.
Hello rhall. Thank you for the help. Can you please attach zipped export of this job.
Hi @Pooja D, apologies but I am afraid I do not have this job anymore. I wrote it and threw it away. However, all of the information you need is included in the post.