Receiving java class object in Python - java

I've Java .jar file which has several classes defined and there is python framework which intends to pick any class from it, instantiate it's object and invoke it's method. To do this I'm using py4j JavaGateway().
At python side:
from py4j.java_gateway import JavaGateway
gateway = JavaGateway()
obj_rcvd = gateway.entry_point.getObj("pkg.in.jar", "className", java_list)
boo = pkg.in.jar.className(obj_rcvd)
"""
this typecast fails as python env doesn't know about pkg from jar. Can we import java's jar file in Python? Also if we can, do we really need to call JVM to get objects?
"""
At Java side:
import py4j.GatewayServer;
import java.lang.reflect.*;
import java.util.*;
public class EntryPoint {
public static Object getObj(String pkgName, String className, List args) {
Object obj = null;
try {
Class cls2 = Class.forName(pkgName + '.' + className);
int num_of_args = args.size();
Class[] cArg = new Class[num_of_args];
Object[] cArg_val = new Object[num_of_args];
/* code to parse args to fill cArg and cArg_val */
Constructor ctor = cls2.getDeclaredConstructor(cArg);
obj = ctor.newInstance(cArg_val);
}
catch (ClassNotFoundException x) {
x.printStackTrace();
}
/* other exception catchers */
return obj; // this is general Object type, hence I need to typecast in python
}
public static void main(String[] args) {
GatewayServer gatewayServer = new GatewayServer(new EntryPoint());
gatewayServer.start();
System.out.println("Gateway Server Started");
}
I tried returning actual class object from Java(hard-coded for one case for debugging) but it too doesn't get recognized in Python. Please suggest if this approach to invoke methods of java jar in python is feasible.

Related

Java ClassLoading from client's memory

I am developing a client-server application using Java Sockets where at some point the client has to download some classes from the server (through the already bound Socket) and use them. I am able to read the class file bytes and send them to the client. So then, the client has to use a ClassLoader to load the classes from memory. So, in my opinion, the problem is not really related to Sockets, but it is about custom class loading (you will see why I say that, later in this post).
My setup is as follows: the client project with a single package named client with a single class in it, named LiveClientTest and then the server project with a single package named server with 3 classes in it: ClientMain, LiveServerTest (the entry point) and MyStrings. In short, all server-side code is under the package server and all client-side under the client. Each of the two packages is in a different project also.
The problem occurs when the custom ClassLoader of the client (named LiveClientTest.MemoryClassLoader) tries to load a non-static nested class (named MyStrings.NestedNonStaticSubclass) which refers to its enclosing class (named MyStrings) before constructing the object (of type MyStrings.NestedNonStaticSubclass). Although the classes compile fine, the error appears at runtime, while loading the class MyStrings.NestedNonStaticSubclass with the custom ClassLoader. I know it sounds weird, but you can see what I mean if you take a look at the code.
Server side code:
LiveServerTest class (entry point):
package server;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import javax.swing.JOptionPane;
public class LiveServerTest {
//Convert class binary name representation to file path:
public static String classNameToResourcePath(final String className) {
return className.replace('.', File.separatorChar) + ".class";
}
//Get directory location of the given class (packages not included):
public static Path getDirectoyPath(final Class clazz) throws URISyntaxException {
return new File(clazz.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath();
}
//Get absolute file location of the given class:
public static Path getClassFilePath(final Class c) throws URISyntaxException {
return getDirectoyPath(c).resolve(classNameToResourcePath(c.getName()));
}
public static void main(final String[] args) {
ServerSocket srv = null;
try {
srv = new ServerSocket(Integer.parseInt(Objects.requireNonNull(JOptionPane.showInputDialog(null, "Enter host port number:"))));
System.out.println("Waiting for client connection...");
try (final Socket sck = srv.accept();
final OutputStream os = sck.getOutputStream()) {
srv.close();
srv = null;
//These are the classes we need the client to load:
final Class[] clientClasses = new Class[] {
ClientMain.class,
MyStrings.class,
MyStrings.NestedStatic.class,
MyStrings.NestedNonStaticSubclass.class
};
System.out.println("Sending all client classes' bytes to client...");
final DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(clientClasses.length);
for (final Class clazz: clientClasses) {
final byte[] contents = Files.readAllBytes(getClassFilePath(clazz));
dos.writeUTF(clazz.getName());
dos.writeInt(contents.length);
dos.write(contents);
}
System.out.println("Main application now starts...");
//Here would go the server side code for the client-server application.
}
}
catch (final IOException | URISyntaxException | RuntimeException x) {
JOptionPane.showMessageDialog(null, x.toString());
}
finally {
System.out.println("Done.");
try { if (srv != null) srv.close(); } catch (final IOException iox) {}
}
}
}
ClientMain class:
package server;
import java.io.DataInputStream;
import java.net.Socket;
public class ClientMain {
//This method is called by the client to start the application:
public static void main(final Socket sck,
final DataInputStream dis,
final ClassLoader loader,
final String[] args) {
System.out.println("Running...");
//Here would go the client side code for the client-server application.
//Just test that all the classes are loaded successfully:
System.out.println(new MyStrings("A", "B", "C").new NestedNonStaticSubclass().getFunction().apply(2)); //Should print "C".
}
}
MyStrings class:
package server;
import java.util.function.IntFunction;
public class MyStrings {
public static class NestedStatic {
private final IntFunction<String> f;
public NestedStatic(final IntFunction<String> f) {
this.f = f;
}
public IntFunction<String> getFunction() {
return f;
}
}
//This class produces the error when loaded:
public class NestedNonStaticSubclass extends NestedStatic {
public NestedNonStaticSubclass() {
super(i -> getString(i)); //Here we refer to MyStrings object before constructing the NestedNonStaticSubclass object.
}
}
private final String[] array;
public MyStrings(final String... array) {
this.array = array.clone();
}
public String getString(final int i) {
return array[i];
}
}
Client side code:
package client;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects;
import javax.swing.JOptionPane;
public class LiveClientTest {
public static class MemoryClassLoader extends ClassLoader {
//The key is the name of the class, and value is the compiled class file bytes:
private final HashMap<String, byte[]> classByteCode = new HashMap<>();
#Override
public /*synchronized*/ Class findClass(final String name) throws ClassNotFoundException {
try {
return super.findClass(name);
}
catch (final ClassNotFoundException cnfx) {
if (!classByteCode.containsKey(name))
throw new ClassNotFoundException(name);
final byte[] byteCode = classByteCode.get(name);
return defineClass(name, byteCode, 0, byteCode.length);
}
}
//Try to load all classes that are downloaded (with readClass):
ArrayList<String> loadClasses() {
final ArrayList<String> classNames = new ArrayList<>(classByteCode.keySet());
int oldSize;
do {
oldSize = classNames.size();
final Iterator<String> classNamesIter = classNames.iterator();
while (classNamesIter.hasNext()) {
final String className = classNamesIter.next();
try {
loadClass(className);
classNamesIter.remove();
}
catch (final ClassNotFoundException x) {
}
}
}
while (classNames.size() < oldSize); //If we reached a point where there are some classes that can not be loaded, then quit...
return classNames; //Return the classes that failed to be loaded (if any) (should be empty).
}
//Read class bytes from supplied DataInputStream:
void readClass(final DataInputStream dis) throws IOException {
final String name = dis.readUTF();
final byte[] contents = new byte[dis.readInt()];
int i = 0, n = dis.read(contents);
//Make sure all 'contents.length' multitude of bytes are read:
while (n >= 0 && (i + n) < contents.length) {
i += n;
n = dis.read(contents, i, contents.length - i);
}
if (/*n < 0 ||*/ (i + n) != contents.length)
throw new IOException("Unfinished class input (" + name + " - " + contents.length + ").");
classByteCode.put(name, contents);
}
}
public static void main(final String[] args) {
try {
final String host = Objects.requireNonNull(JOptionPane.showInputDialog(null, "Enter host name or address:"));
final int port = Integer.parseInt(Objects.requireNonNull(JOptionPane.showInputDialog(null, "Enter host port number:")));
try (final Socket sck = new Socket(host, port);
final InputStream is = sck.getInputStream()) {
final DataInputStream dis = new DataInputStream(is);
final MemoryClassLoader loader = new MemoryClassLoader();
//Download all classes and put them into the class loader:
System.out.println("Downloading...");
for (int i = dis.readInt(); i > 0; --i)
loader.readClass(dis);
//Load all downloaded classes from the class loader:
System.out.println("Loading...");
System.out.println("Failed to load: " + loader.loadClasses() + '.');
//Call main method in main downloaded class:
loader
.loadClass("server.ClientMain") //package is from server side.
.getDeclaredMethod("main", Socket.class, DataInputStream.class, ClassLoader.class, String[].class)
.invoke(null, sck, dis, loader, args);
}
}
catch (final IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | RuntimeException x) {
x.printStackTrace();
JOptionPane.showMessageDialog(null, x);
}
}
}
Client side output:
Downloading...
Loading...
Exception in thread "main" java.lang.ClassFormatError: Illegal field name "server.MyStrings$this" in class server/MyStrings$NestedNonStaticSubclass
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at client.LiveClientTest$MemoryClassLoader.findClass(LiveClientTest.java:30)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at client.LiveClientTest$MemoryClassLoader.loadClasses(LiveClientTest.java:44)
at client.LiveClientTest.main(LiveClientTest.java:89)
So my question is:
Why does the code fail with a ClassFormatError, what does that mean, and how to avoid it in this particular scenario?
My question is not:
What alternatives exist? (such as using a URLClassLoader, or alternative ways of class loading from memory other than a custom ClassLoader, etc...)
How to reproduce:
I am using JRE 1.8.0_251 (and I would like this to work for 1.8), so I think you must put the source files in different projects (one for the client and one for the server) in order to make sure that the client does not already have direct visibility of the server's classes while class-loading them.
Run the server's main class server.LiveServerTest and input a port number for the server in the dialog that pops up. Then, run the client's main class client.LiveClientTest and enter localhost for the host (first dialog that pops up) and then the port number of the server (second dialog that pops up).
The stack trace will be in your CLI (through System.err) and not in a GUI.
The code will not work if you build the projects into jar files, but it is written to work for plain class files into directories, for simplicity. For example, in NetBeans, don't build into jar files, but rather click Run File for each entry point.
The built classes should have their file's .class extention in lower case.
Warning:
If you put the source files in different packages but in the same project, or even worse in the same package, then the class loading might succeed without errors because:
I am using the default constructor of the ClassLoader class in my LiveClientTest.MemoryClassLoader class, which means the parent class loader is the system class loader.
The LiveClientTest.MemoryClassLoader.findClass method first searches the parent ClassLoader and then, if that fails, it searches the downloaded classes. To my knowledge, this is the suggested way of implementing this, mainly because the ClassLoader class (which is the parent class of my LiveClientTest.MemoryClassLoader class) caches already defined classes.
References:
How to load JAR files dynamically at Runtime?
In which scenarios the remote class loading are needed?
Does the Java ClassLoader load inner classes?
Java - Get a list of all Classes loaded in the JVM
Java: How to load a class (and its inner classes) that is already on the class path?
Create new ClassLoader to reload Class
How to use classloader to load class file from server to client
Custom Java classloader and inner classes
ClassFormatError in java 8?
JVM Invalid Nested Class Name?
https://bugs.openjdk.java.net/browse/JDK-8145051
https://www.programming-books.io/essential/java/implementing-a-custom-classloader-0f0fe95cf7224c668e631a671eef3b94
https://www.baeldung.com/java-classloaders
https://www.infoworld.com/article/2077260/learn-java-the-basics-of-java-class-loaders.html
https://www.oracle.com/technical-resources/articles/javase/classloaders.html
https://www.ibm.com/developerworks/library/j-onejar/index.html
I am new to class loading, so please don't take my words for granted.
Huge post, because of the divided code of the MRE. Sorry. I tried to make the code as minimal as possible.

Generating a new java class by writing java program in another java class [duplicate]

This question already has answers here:
How do I create a file and write to it?
(35 answers)
Closed 3 years ago.
I want to create a java program that generates another java class in the same project. For example in the class Dragon.java, i want to write java code that creates another java class called fire.java. I do not want to use any GUI from eclipse, just pure code that generates another class from the execution of written programming in java.
I have tried making objects of a non existent class in hopes of the program automatically producing a class with that name.
Again, it doesn't have to be just a java class, is there a way to make other forms of files also? for example fol.flow, or of different names.
Creating a new Java file is easy. You can use any FileWriter technique. But what need to be taken care of is that new Java file is valid java file and can be compiled to class file.
This link has working example of doing the same.
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class MakeTodayClass {
Date today = new Date();
String todayMillis = Long.toString(today.getTime());
String todayClass = "z_" + todayMillis;
String todaySource = todayClass + ".java";
public static void main (String args[]){
MakeTodayClass mtc = new MakeTodayClass();
mtc.createIt();
if (mtc.compileIt()) {
System.out.println("Running " + mtc.todayClass + ":\n\n");
mtc.runIt();
}
else
System.out.println(mtc.todaySource + " is bad.");
}
public void createIt() {
try {
FileWriter aWriter = new FileWriter(todaySource, true);
aWriter.write("public class "+ todayClass + "{");
aWriter.write(" public void doit() {");
aWriter.write(" System.out.println(\""+todayMillis+"\");");
aWriter.write(" }}\n");
aWriter.flush();
aWriter.close();
}
catch(Exception e){
e.printStackTrace();
}
}
public boolean compileIt() {
String [] source = { new String(todaySource)};
ByteArrayOutputStream baos= new ByteArrayOutputStream();
new sun.tools.javac.Main(baos,source[0]).compile(source);
// if using JDK >= 1.3 then use
// public static int com.sun.tools.javac.Main.compile(source);
return (baos.toString().indexOf("error")==-1);
}
public void runIt() {
try {
Class params[] = {};
Object paramsObj[] = {};
Class thisClass = Class.forName(todayClass);
Object iClass = thisClass.newInstance();
Method thisMethod = thisClass.getDeclaredMethod("doit", params);
thisMethod.invoke(iClass, paramsObj);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
At first I thought you wanted code generation, but you simply want to write to files or create them?
The simplest code to create file and write to it:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class Testing {
public static void main(String[] args) throws IOException {
Files.writeString(Paths.get("D://output.txt"), "some text to write", StandardOpenOption.CREATE);
}
}
It uses only java standard classes, you don't need any libraries or anything external. Just make sure to write to the valid path, where you have access.
If you want to generate files with java code, you can just do it with the method above, but creating the String with code content is really hard, there are libraries for it and they are not easy to use for beginners. For example javapoet. I personally used javaparser, it has a lot of other possibilities besides generating code.

Nashorn: How to pre-set Java.type()-vars inside of Java before JavaScript execution?

I am currently executing my JavaScript-scripts with this java code:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("awesome_script.js"));
I need to call Java functions from JavaScript, so I defined this at the top of my awesome_script.js file:
var first = Java.type('io.github.awesomeprogram.FirstClass');
var second = Java.type('io.github.awesomeprogram.SecondClass');
var extra = Java.type('io.github.awesomeprogram.ExtraClass');
I can then call some methods from these classes, e.g.:
second.coolmethod("arg1",2);
My problem is now that I need to use many java classes inside of my scripts. I also have a lot of scripts and I think it is very inefficient to define every single one of this classes in every script.
So I am looking for a solution to create the objects created inside of JavaScript with Java.type() inside of Java and then pass them to the script I want to execute.
How can I do this?
Thanks in advance!
You may want to avoid using the "internal" classes in packages like "jdk.internal.", "jdk.nashorn.internal.". In jdk9, dynalink is an API ("jdk.dynalink" has exported packages). In jdk9, you can call jdk.dyanlink.beans.StaticClass.forClass(Class) [ http://download.java.net/java/jdk9/docs/jdk/api/dynalink/jdk/dynalink/beans/StaticClass.html#forClass-java.lang.Class- ] to construct "type" objects and expose those as global variables to the script engine. For jdk8, you could pre-eval a script that uses Java.type(String) calls before evaluating "user" scripts. You can also call "Java.type" function from Java code.
Solution for jdk9:
import jdk.dynalink.beans.StaticClass;
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
e.put("AList", StaticClass.forClass(java.util.ArrayList.class));
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
Solution for jdk8:
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// eval a "boot script" before evaluating user script
// Note that this script could come from your app resource URL
e.eval("var AList = Java.type('java.util.ArrayList')");
// now evaluate user script!
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
Alternative solution for jdk8:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// get Java.type function as object
JSObject javaTypeFunc = (JSObject) e.eval("Java.type");
// you can javaTypeFunc from java code many times
Object alType = javaTypeFunc.call(null, "java.util.ArrayList");
// expose that as global
e.put("AList", alType);
// now evaluate user script!
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
After quite a bit of research I found a way to put global variables in the ScriptEngine before executing: The Java Scripting API (Oracle Docs)
This enabled me to put any object I want into a global variable. However, I still needed a way to get the Object that Java.type() creates inside of Java. So I wrote a test script which returns one of this objects and I found out it is an object of the type jdk.internal.dynalink.beans.StaticClass. This class has an constructor which takes a ordinary Class as an argument. Sadly, this constructor is not usable in my code because it is not visible. To bypass this I used reflection and made this method:
public StaticClass toNashornClass(Class<?> c) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Class<?> cl = Class.forName("jdk.internal.dynalink.beans.StaticClass");
Constructor<?> constructor = cl.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
StaticClass o = (StaticClass) constructor.newInstance(c);
return o;
}
If I pass the Class of the object I want as a global variable I just need to call toNashornClass(Example.class); and put the resulting object into a global var with engine.put("example",object);
It works fine. I can use the example var completely like a var created by Java.type().

How to block access to some classes, when executing groovy scripts from java?

I'm pretty new to groovy, and scripting in java generally, and I really
hope there is a simple solution for my problem.
In our application, the users can execute groovy scripts which they write
themselves, and we need to control what those scripts can and can not do.
I read a lot of stuff about sandboxing groovy, but either I am looking at
wrong places or I am overlooking the obvious.
To make it simple, I have a small example which demonstrates the problem.
This is my class loader which should prevent java.lang.System from being
loaded and available to scripts:
public class MyClassLoader extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("java.lang.System")) {
throw new ClassNotFoundException("Class not found: " + name);
}
return super.loadClass(name);
}
}
And this is a simple program that tries to call System.currentTimeMillis():
public static void main(String[] args) {
String code = "java.lang.System.currentTimeMillis();";
ClassLoader classLoader = new MyClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
GroovyShell shell = new GroovyShell();
Script script = shell.parse(code);
Object result = script.run();
log.debug(result);
}
MyClassLoader throws exceptions for java.lang.SystemBeanInfo
and java.lang.SystemCustomizer, but the code executes.
Same thing happens if I use javax.script classes:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("Groovy");
Object o = engine.eval(code);
log.debug(o);
And if I try it with JavaScript engine, it works as expected (just replace
"Groovy" with "JavaScript" in the above example).
Can anyone help me with this? BTW, I'm using groovy-all-1.8.8.jar, with
jdk1.7.0_55.
Thanks
I can recommend Groovy Sandbox for this purpose. In contrast to SecureASTCustomizer it will check if an execution is allowed dynamically at runtime. It intercepts every method call, object allocations, property/attribute access, array access, and so on - and you thus have a very fine grained control on what you allow (white-listing).
Naturally the configuration on what is allowed is very important. For example you may want to allow using Strings and use methods like substring, but probably not the execute method on String, which could be exploited with something like 'rm -R ~/*'.execute().
Creating a configuration that is really safe is a challenge, and it is more difficult the more you allow.
Downside of the Groovy Sandbox is that the code must run with the interceptor registered and you will have a performance penalty during execution.
This image [1] shows an example from a project where we used Groovy Sandbox for Groovy code entered by the user. The code is run to valide the script - so if the statement there would actually be executed as part of it, the application would have exited before I could do the screenshot ;)
Perhaps you'd be interested in using a SecureASTCustomizer in conjunction with a CompilerConfiguration. If you are concerned with security, an explicit white list might be better than a black list.
def s = new SecureASTCustomizer()
s.importsWhiteList = [ 'a.legal.Klass', 'other.legal.Klass' ]
def c = new CompilerConfiguration()
c.addCompilationCustomizers(s)
def sh = new GroovyShell(c)
Take a look at that class, it contains a lot of options that are ready to use.
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
public class SandboxGroovyClassLoader extends ClassLoader {
public SandboxGroovyClassLoader(ClassLoader parent) {
super(parent);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("java.lang.System"))
return null;
return super.loadClass(name);
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("java.lang.System"))
return null;
return super.loadClass(name, resolve);
}
static void runWithGroovyClassLoader() throws Exception {
System.out.println("Begin runWithGroovyClassLoader");
String code = "def hello_world() { java.lang.System.currentTimeMillis(); };";
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class<?> scriptClass = groovyClassLoader.parseClass(code);
Object scriptInstance = scriptClass.newInstance();
Object result = scriptClass.getDeclaredMethod("hello_world", new Class[] {}).invoke(scriptInstance, new Object[] {});
System.out.println(result);
groovyClassLoader.close();
System.out.println("End runWithGroovyClassLoader");
}
static void runWithSandboxGroovyClassLoader() throws Exception {
System.out.println("Begin runWithSandboxGroovyClassLoader");
ClassLoader parentClassLoader = SandboxGroovyClassLoader.class.getClassLoader();
SandboxGroovyClassLoader classLoader = new SandboxGroovyClassLoader(parentClassLoader);
String code = "def hello_world() { java.lang.System.currentTimeMillis(); };";
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader);
Class<?> scriptClass = groovyClassLoader.parseClass(code);
Object scriptInstance = scriptClass.newInstance();
Object result = scriptClass.getDeclaredMethod("hello_world", new Class[] {}).invoke(scriptInstance, new Object[] {});
System.out.println(result);
groovyClassLoader.close();
System.out.println("End runWithSandboxGroovyClassLoader");
}
static void runWithSandboxGroovyShellClassLoader() throws Exception {
System.out.println("Begin runWithSandboxGroovyShellClassLoader");
String code = "java.lang.System.currentTimeMillis();";
ClassLoader parentClassLoader = SandboxGroovyClassLoader.class.getClassLoader();
SandboxGroovyClassLoader classLoader = new SandboxGroovyClassLoader(parentClassLoader);
Thread.currentThread().setContextClassLoader(classLoader);
GroovyShell shell = new GroovyShell();
Script script = shell.parse(code);
Object result = script.run();
System.out.println(result);
System.out.println("End runWithSandboxGroovyShellClassLoader");
}
public static void main(String[] args) throws Exception {
runWithGroovyClassLoader();
runWithSandboxGroovyClassLoader();
runWithSandboxGroovyShellClassLoader();
}
}
Is it what you want ?

Loading a class from an external jar

This is my first java program, so please excuse me if its too naive.
I have a 3rd party jar. I want to instantiate a class in the jar and be able to use its methods. Some details about the class in the jar:
Class File: rediff.inecom.catalog.product.CSVAPI
Constructor: CSVAPI()
Method: UpdateCSVAPI(key, csvpath)
Return: String
I have written the following program:
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.io.IOException;
class MyLoaderClass{
public void myLoaderFunction(){
File file = new File("vendorcatalogapi.jar");
try {
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("rediff.inecom.catalog.product.CSVAPI");
Object cls_object = cls.newInstance();
System.out.println(cls_object);
String output = cls_object.UpdateCSVAPI(12345,"myfile.csv");
System.out.println(output);
System.out.println("try");
}
catch (Exception e) {
System.out.println("catch");
e.printStackTrace();
}
}
public static void main(String args[]){
new MyLoaderClass().myLoaderFunction();
}
}
I am trying to compile it using:
javac -cp vendorcatalogapi.jar temp.java
But I am getting the following error:
temp.java:17: error: cannot find symbol
String output = cls_object.UpdateCSVAPI(12345,"myfile.csv");
^
symbol: method UpdateCSVAPI(int,String)
location: variable cls_object of type Object
1 error
Looks like the object is not correctly initialized. Please can someone help me with the correct way of doing it
If this is your first java program, then loading the class dynamically is probably overkill. Just use it normally and let the default class loader load it:
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.io.IOException;
import rediff.inecom.catalog.product.CSVAPI;
class MyFirstClass{
public void myFunction() {
CSVAPI cvsapi = new CSVAPI();
System.out.println(cvsapi);
String output = cvsapi.UpdateCSVAPI(12345,"myfile.csv");
System.out.println(output);
System.out.println("Success!");
}
public static void main(String args[]){
new MyFirstClass().myFunction();
}
}
Compile (note that the source code file name must match the class name):
javac -cp vendorcatalogapi.jar MyFirstClass.java
Run:
java -cp .:vendorcatalogapi.jar MyFirstClass (on Unix based)
java -cp .;vendorcatalogapi.jar MyFirstClass (on Windows)
You have to let the compiler know that cls_object is an instance of CSVAPI. If you don't, you can only use the object methods (toString, equals, etc.).
To do this, you can do the following:
rediff.inecom.catalog.product.CSVAPI cls_object = (rediff.inecom.catalog.product.CSVAPI) cls.newInstance();
Please, note that you need to have CSVAPI in your classpath!
Object class doesnt know the methods of rediff.inecom.catalog.product.CSVAPI class.
Class cls = cl.loadClass("rediff.inecom.catalog.product.CSVAPI");
Object cls_object = cls.newInstance();
So, explicit casting is required
rediff.inecom.catalog.product.CSVAPI object =
(rediff.inecom.catalog.product.CSVAPI) cls.newInstance();
will do the job.

Categories