How to accept and execute arbitrary Java code - java

Many Websites allow a user to type in Java code and run it. How does a program accept Java written externally/at run time and run it?
The only/closest answer i see on StackOverflow is from 5 years ago about Android development that recommended using Janino (Compile and execute arbitrary Java string in Android). Is this still the way to go? Has a better approach (like something built into Java) appeared in the last half decade?
If it helps, I'm building a training app for my students. The code is short (a few methods, maybe 20 lines max) and they must use standard libraries (no need to worry about importing things from maven, etc.).
Like similar online coding sites, I'd like to return the output of the run (or compilation failure).
An example use case:
Webpage says "The code below has an error, try to fix it." A text box contains code. A semicolon is missing.
User modifies the code and presses submit.
The Webpage either returns a compile error message or success.
Another use case would be for me to execute unit tests on the code they submitted and return the result. The point being, I give the user feedback on the code compilation/run.

Here is a small example to use the Java compiler interface
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class Compiler {
public static void main(String[] args) throws Exception {
// compile the java file
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null, "D:\\development\\snippets\\Test.java");
System.out.println("compiler result " + result);
// load the new class
File classesDir = new File("D:\\development\\snippets\\");
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { classesDir.toURI().toURL() });
Class<?> cls = Class.forName("Test", true, classLoader);
// invoke a method of the class via reflection
Object instance = cls.getDeclaredConstructors()[0].newInstance();
Method testMethod = cls.getMethod("test");
String testMethodResult = (String) testMethod.invoke(instance);
System.out.println(testMethodResult);
}
}
And here the test class
public class Test {
public String test() {
return "String from test.";
}
}
Running the Compiler class returns
compiler result 0
String from test.

Related

GraalVM - embedding python multi-file project in java

I couldn't find a solution create a polyglot source out of multiple files in GraalVM.
What exactly I want to achieve:
I have a python project:
my-project:
.venv/
...libs
__main__.py
src/
__init__.py
Service.py
Example sourcecode:
# __main__.py
from src.Service import Service
lambda url: Service(url)
# src/Service.py
import requests
class Service:
def __init__(self, url):
self.url = url
def invoke(self):
return requests.get(self.url)
This is very simple example, where we've got an entry-point script, project is structured in packages and there is one external library (requests).
It works, when I run it from command-line with python3 __main__.py, but I can't get it work, when embedding it in Java (it can't resolve imports).
Example usage in java:
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import java.io.File;
import java.io.IOException;
public class Runner {
public static void main(String[] args) throws IOException {
Context context = Context.newBuilder("python")
.allowExperimentalOptions(true)
.allowAllAccess(true)
.allowIO(true)
.build();
try (context) {
// load lambda reference:
Value reference = context.eval(Source.newBuilder("python", new File("/path/to/my-project/__main__.py")).build());
// invoke lambda with `url` argument (returns `Service` object)
Value service = reference.execute("http://google.com");
// invoke `invoke` method of `Service` object and print response
System.out.println("Response: " + service.getMember("invoke").execute());
}
}
}
It fails with Exception in thread "main" ModuleNotFoundError: No module named 'src'.
The solution works for javascript project (having similar index.js to __main__.py, its able to resolve imports - GraalVM "sees" other project's files, but somehow it doesn't, when using python.
I found out, that python is able to run zip package with project inside, but this also doesn't work with GraalVM.
Is there any chance to accomplish it? If not, maybe there is a similar tool to webpack for python (if I could create a single-file bundle, it should also work).
Btw, I don't know python at all, so I may missing something.
Thanks for any help!

Serializing a JRuby CompiledScript in Java

I have a Ruby script that I'd like to run at the startup of my Java program.
When you tell the ScriptEngine to evaluate the code for the first time, it takes a while. I'm under the impression that the reason it takes this long is because it first needs to compile the code, right?
I found that you can compile Ruby code, and then evaluate it later. The evaluation itself is fast - the compilation part is the slow one. Here I am compiling:
jruby = new ScriptEngineManager().getEngineByName("jruby");
Compilable compilingEngine = (Compilable)jruby;
String code = "print 'HELLO!'";
CompiledScript script;
script = compilingEngine.compile(code);
This snippet is what takes a while. Later when you evaluate it, it is fine.
So of course, I was wondering if it would be possible to "save" this compiled code into a file, so in the future I can "load" it and just execute it without compiling again.
As others have said, this is not possible with CompiledScript. However, with JRuby you have another option. You can use the command line tool jrubyc to compile a Ruby script to Java bytecode like so:
jrubyc <scriptname.rb>
This will produce a class file named scriptname.class. You can run this class from the command line as if it were a normal class with a main(String[] argv) method (note: the jruby runtime needs to be in the classpath) and you can of course load it into your application at runtime.
You can find more details on the output of jrubyc here: https://github.com/jruby/jruby/wiki/JRubyCompiler#methods-in-output-class-file
According to this, no.
"Unfortunately, compiled scripts are not, by default, serializable, so they can't be pre-compiled as part of a deployment process, so compilation should be applied at runtime when you know it makes sense."
I think some really easy cache will solve your problem:
class CompiledScriptCache {
static {
CompiledScriptCache INSTANCE = new CompiledScritCache();
}
publich static CompiledScriptCache get(){
retrun INSTANCE;
};
List<CompiledScript> scripts = new ArrayList<>();
public CompiledScript get(int id){
return scripts.get(id);
}
public int add(String code){
ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby");
Compilable compilingEngine = (Compilable)jruby;
CompiledScript script;
script = compilingEngine.compile(code);
scripts.add(script);
return scripts.size()-1;
}
}
update
I thought this question was about avoiding to comile the source more than once.
Only other approach I could imagine is to create Java-Classes and make a cross-compile:
https://github.com/jruby/jruby/wiki/GeneratingJavaClasses

Java - Eclipse IDE throwing warnings and errors but the same errors do not appear in Netbeans

I am attempting to create an applet to replace my ActiveX control because it recently started having problems even though it has worked for years. All this will do is read data from a serial port, a barcode scanner in my case, and pass the data to an input box. Also any pointers on ways I could make this better would be greatly appreciated.
Code:
package checkin;
import jssc.SerialPort;
import jssc.SerialPortException;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import netscape.javascript.*;
import java.applet.Applet;
public class MainSerial extends Applet {
static SerialPort port;
static JSObject window;
public static void main(String[] args) { //WARNING
SerialPort port = new SerialPort("COM1");
try {
port.openPort();
port.setParams(9600, 8, 1, 0);
int mask = SerialPort.MASK_RXCHAR + SerialPort.MASK_CTS + SerialPort.MASK_DSR;
port.setEventsMask(mask);
port.addEventListener(new SerialPortReader()); //ERROR
} catch (SerialPortException ex) {
window.eval("alert('Could not open port.\nThe port may be in use by another program.')");
}
}
class SerialPortReader implements SerialPortEventListener {
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR()) {
try {
byte buffer[] = port.readBytes();
int x = 0;
int count = buffer.length;
StringBuilder data = new StringBuilder();
while(x < count) {
data.append(buffer[x]);
x++;
}
window.eval("getSerialData('" + data.toString() + "')");
} catch (SerialPortException ex) {
window.eval("alert('Port could not be read.')");
}
}
}
}
}
Error Thrown with Eclipse:
The serializable class MainSerial does not declare a static final serialVersionUID field of type Long
Warning thrown with Eclipse:
No enclosing instance of MainSerial is accessible. Must qualify the allocation with an enclosing instance of type MainSerial.
This is my first attempt at Java, please excuse my ignorance. What doesn't make sense to me is why do I see these errors in Eclipse but not NetBeans? Also, if the line
port.addEventListener(new SerialPortReader());
Is in that specific parent why would I need to reinitialize it? I'm sure I'm missing something small and probably quite obviously but any help is greatly appreciated. The reason I am attempting to create an applet is due to the increasing problems with my ActiveX control.
Eclipse's compiler has a lot of configurable warnings and errors that other compilers may or may not have. In general the default settings are quite reasonable and you should try to fix them if possible. Many can easily be fixed using the Quick Fix feature: right-click on an error in the Markers (or Problems) view and choose Quick Fix....
Alternatively, you can right-click on the underline (yellow or red) in the code where the error or warning is, and choose Quick Fix....
Not every warning/error has a quick fix, but many do.
If you want to disable a particular warning or error, right-click your project and open the Properties dialog. In there, look under Java Compiler > Errors/Warnings to find all of the configurable ones. You'll have to Enable project specific settings unless you want the same settings to apply to all projects in your workspace.
Eclipse typically throws a warning around ServialVersionUID when you create a serializable object without specifying a serial version ID.
having serial version IDs is a best practice, not a strict requirement, and differend IDEs behave differently about it. More info on why you may want it can be found here. Please note: this should be a warning, not an error. If it is a warning, there are three ways of avoiding this:
Ask Eclipse to generate a default ID for you (it will generate private static final long serialVersionUID = 1L;)
Generate one your self
Tell Eclipse to ignore the warning (adding the #SuppressWarnings("serial") annotation)
For the other issue, there is a wide liteerature out there. Here is a link you may want to read.
Hope this helps.

Running a user created Java class

In my main program I am allowing users to create Java classes and storing them in a .java file within the package UserInputs. I am now looking for a way to instantiate the user created class within my main program and also running the public methods within the class.
Here is the code which gets executed when the user presses a JButton to finish creating their class.
public void actionPerformed(ActionEvent e) {
if(e.getSource() == inputButt.getButtons()){
try{
PrintWriter writer = new PrintWriter("C:/Users/human/Desktop/UserInputTest/src/UserInputs/UserCreatedClass.java", "UTF-8");
writer.println(textArea.getText());
writer.close();
}catch(Exception except){
except.printStackTrace();
}
}
}
You need to compile the file at runtime. Maybe this or this post here on SO helps you.
What the first link says is that you should use the Java 6 Compiler API. What you need to do is this:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compilationResult = compiler.run(null, null, null, fileToCompile);
Where fileToCompile is the path to your file, in your case "C:/Users/human/Desktop/UserInputTest/src/UserInputs/UserCreatedClass.java". Then you can execute the code via Reflection.
I would be very carefully with letting people create and execute their own Java code, though. I don't know what you plan to do but if you are running this code on a server, I would not recommend doing such things. In case this application should run locally on the clients computer (so they can only harm themselves) this should not be a problem. Otherwise I would not let them program what they want.
You might also want to consider Groovy compiler which is almost fully compatible with Java syntax, more functional and has simpler API. Example from a Groovy page:
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
Class groovyClass = loader.parseClass(new File("src/test/groovy/script/HelloWorld.groovy"));
// let's call some method on an instance
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
Object[] args = {};
groovyObject.invokeMethod("run", args);

Weird error with Locale.getISOCountries()

I'm using this code:
for (final String code : Locale.getISOCountries())
{
//stuff here
}
But on compile I get this error:
[ERROR] Line 21: No source code is available for type java.util.Locale; did you forget to inherit a required module?
And then a stack trace of compiler errors.
I'm doing both of these imports at the beginning of the class:
package com.me.example;
import java.util.Locale;
import java.util.*;
What can be wrong?
In Netbeans i see the autocomplete options and no syntax error for the Locale object...
Something screwy with your setup, the folllowing program works fine for me.
import java.util.*;
import java.util.Locale;
public class Donors {
public static void main (String [] args) {
for (final String code : Locale.getISOCountries()) {
System.out.println (code);
}
}
}
The fact that it's asking for source code leads me to believe that it's trying to compile or run it in some sort of debugging mode. You shouldn't need the source code for java.util.* to compile, that's just bizarre.
See if my simple test program works in your environment, then try looking for something along those lines (debugging options). Final step: compile your code with the baseline javac (not NetBeans).
UPDATE:
Actually, I have found something. If you are creating GWT applications, I don't think java.util.Locale is available on the client side (only the server side). All of the references on the web to this error message point to GWT and its limitations on the client side which are, after all, converted to Javascript goodies, so cannot be expected to support the entire set of Java libraries.
This page here shows how to do i18n on GWT apps and there's no mention of java.util.Locale except on the server side.
Looks like there might be something fishy in your build environment, as Locale.getISOCountries() should work just fine. Try compiling a small test program manually and see if you get the same error.
Definitely try to boil this down to a minimum, three-line program (or so), compile from the command-line, then put that class into your IDE and see if you still get the error, and if not, then change/add one line at a time until you have the original failing program, looking for what causes the problem. I'm thinking maybe some other import in your code is importing a Locale class? Why in the world would it be looking for source code?
See what happens when you compile this from the command-line:
import java.util.*;
public class LocaleTest {
public static void main(String[] args) {
Locale.getISOCountries();
}
}

Categories

Resources