Possible to have code run just by importing class in Java? - java

Working on this one-class library where I need to disable buffering on stderr and stdout, if client imports that class, using something like
// stderr
FileOutputStream fderr = new FileOutputStream(FileDescriptor.err);
// stdout
FileOutputStream fdout = new FileOutputStream(FileDescriptor.out);
// stderr buffer of size 1
BufferedOutputStream errBuf = new BufferedOutputStream(fderr, 1);
// stdout buffer of size 1
BufferedOutputStream outBuf = new BufferedOutputStream(fdout, 1);
// add more features and functionality to stderr and stdout
PrintStream errStream = new PrintStream(errBuf);
PrintStream outStream = new PrintStream(outBuf);
// update stderr and stderr
System.setErr(errStream);
System.setOut(outStream);
Came across static initializers, but unfortunately, they only run in particular circumstances, none of which is of interest to me. Is there any way in Java to have the code above run only by importing the containing class (i.e., just the import statement; without invoking any methods, etc)?

You are looking into the wrong place.
If you want that your application sends all stdout/stderr messages into a specifc stream; then you "simply" want to control that on the "JVM startup" level. In other words: you should look into means to manipulate the JVM as early as possible.
You don't want that redirection to be put in place when some of your classes gets "imported"; you want to have a robust way of directly telling the JVM on startup to do that.
But of course: the really sane way of doing such things is to use a logging framework which allows for much better control of what is going on. Logging to stdout/stderr isn't a good approach in the first place!
Edit: given the fact that this is about "training wheels" for students; then simply put a static setup method in some of your library classes (ideally taking to strings that denote file names where stdout/stderr should go to); and instruct your students to call that method as very first thing in their main methods.

Imports are only used at compile time.
You say that static initializers are not of interest. However, static initializers run when your class is loaded. There is no way around it you cannot run code from your class without it being loaded, therefore I would say static initializers are exactly what you want.
The client can load a class without having to instantiate it, for example by using Class.forName
Example
public class Foo
{
static
{
System.out.println("Class foo loaded");
}
}
// different file...
public class Client
{
static
{
try {
Class.forName("Foo");
}catch (ClassNotFoundException e) {
// TODO
}
}
public static void main (String args[]) {
// Nothing to do in this demo. The static intitizlier in
// this class causes Foo to be loaded and thus invokes Foo's
// static initializer
}
}

Related

(de)serialization of parsed Groovy scripts

We want to enable our customers to customize certain aspects of their requests processing, by letting them write something (currently looking at Groovy scripts), then have those scripts saved in a DB and applied when necessary, this way we won't have to maintain all those tiny aspects of processing details that might apply to certain customers only.
So, with Groovy, a naive implementation would go like this:
GroovyShell shell = new GroovyShell(); // prepare execution engine - probably once per thread
(retrieve script body from the DB, when necessary)
Script script = shell.parse(scriptBody); // parse/compile execution unit
Binding binding = prepareBinding(..); script.setBinding(binding); // provide script instance with execution context
script.run(); doSomething(binding);
When run one after the other, step 1 takes approx. 800 msec, step 3 takes almost 2000 msec, and step 5 takes about 150 msec. Absolute numbers will vary, but the relative numbers are quite stable. Assuming that step 1 is not going to be executed per-request, and step 5 number execution time is quite tolerable, I am very much concerned with step 3: parsing the Groovy script instance from the source code. I did some reading across the documentation and code, and some googling as well, but has not thus far discovered any solution, so here's the question:
Can we somehow pre-compile groovy code ONCE, then persist it in the DB and then re-hydrate whenever necessary, to obtain an executable Script instance (that we could also cache when necessary) ?
Or (as I am just thinking now) we could just compile Java code to bytecode and persist it in the Db?? Anyway, I am not so much concerned about particular language used for the scripts, but sub-second execution time is a must.. Thanks for any hints!
NB: I am aware that GroovyShellEngine will likely cache the compiled script; that still risks too long of a delay for first time execution, also risks memory overconsumption...
UPD1: based on excellent suggestion by #daggett, I've modified a solution to look as follows:
GroovyShell shell = new GroovyShell();
final Class<? extends MetaClass> theClass = shell.parse(scriptBody).getMetaClass().getTheClass();
Script script = InvokerHelper.createScript(theClass, binding);
script.run();
this works all fine and well! Now, we need to de-couple metaclass creation and usage; for that, I've created a helper method:
private Class dehydrateClass(Class theClass) throws IOException, ClassNotFoundException {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(stream);
outputStream.writeObject(theClass);
InputStream in = new ByteArrayInputStream(stream.toByteArray());
final ObjectInputStream inputStream = new ObjectInputStream(in);
return (Class) inputStream.readObject();
}
which I've dested as follows:
#Test
void testDehydratedClass() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
RandomClass instance = (RandomClass) dehydrateClass(RandomClass.class).newInstance();
assertThat(instance.getName()).isEqualTo("Test");
}
public static class RandomClass {
private final String name;
public RandomClass() {
this("Test");
}
public RandomClass(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
which passes OK, which means that, in general, this approach is OK.
However, when I try to apply this dehydrateClass approach to theClass, returned by compile phase, I get this exception:
java.lang.ClassNotFoundException: Script1
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:686)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1866)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1749)
at java.io.ObjectInputStream.readClass(ObjectInputStream.java:1714)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1554)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
so, my impression is, that this de-serialization trick will not do any good, if the ClassLoader in question does not already have knowledge of what constitutes a Script1.. seems like the only way to make this kind of approach work is to save those pre-compiled classes somehow somewhere .. or may be learn to serialize them differently
you can parse/compile scripts/classes during editing and store compiled version somewhere - in database, file system, memory, ...
here is a groovy code snippet to compile script/class to a bytecode and then define/load classes from the bytecode.
import org.codehaus.groovy.control.BytecodeProcessor
import org.codehaus.groovy.control.CompilerConfiguration
//bytecode processor that could be used to store bytecode to cache(file,db,...)
#groovy.transform.CompileStatic
class BCP implements BytecodeProcessor{
Map<String,byte[]> bytecodeMap = [:]
byte[] processBytecode(String name, byte[] original){
println "$name >> ${original.length}"
bytecodeMap[name]=original //here we could store bytecode to a database or file system instead of memory map...
return original
}
}
def bcp = new BCP()
//------ COMPILE PHASE
def cc1 = new CompilerConfiguration()
cc1.setBytecodePostprocessor(bcp)
def gs1 = new GroovyShell(new GroovyClassLoader(), cc1)
//the next line will define 2 classes: MyConst and MyAdd (extends Script) named after the filename
gs1.parse("class MyConst{static int cnt=0} \n x+y+(++MyConst.cnt)", "MyAdd.groovy")
//------ RUN PHASE
// let's create another classloader that has no information about classes MyAdd and MyConst
def cl2 = new GroovyClassLoader()
//this try-catch just to test that MyAdd fails to load at this point
// because unknown for 2-nd class loader
try {
cl2.loadClass("MyAdd")
assert 1==0: "this should not happen because previous line should throw exception"
}catch(ClassNotFoundException e){}
//now define previously compiled classes from the bytecode
//you can load bytecode from filesystem or from database
//for test purpose let's take them from map
bcp.bytecodeMap.each{String name, byte[] bytes->
cl2.defineClass(name, bytes)
}
def myAdd = cl2.loadClass("MyAdd").newInstance()
assert myAdd instanceof groovy.lang.Script //it's a script
myAdd.setBinding([x: 1000, y: 2000] as Binding)
assert myAdd.run() == 3001 // +1 because we have x+y+(++MyConst.cnt)
myAdd.setBinding([x: 1100, y: 2200] as Binding)
assert myAdd.run() == 3302
println "OK"

Calling KeyTool, redirecting System.out has no effect

So we want to use the bog-standard keytool utility that ships with a JRE. But rather than going through the trouble of finding the correct path and executable extension, spawning a subprocess, and running the executable, we collectively had the bright idea ("remember, none of us is as dumb as all of us!") to just call KeyTool's main() directly. It's implemented in Java code and also shipped with the JRE, and contains the standard "classpath" exception to the GPL so we can link against it.
Looking at the KeyTool source, there's even some provision made for this sort of thing: there are comments like "if you're calling KeyTool.main() directly in your own Java program, then [helpful reminder]" and the top-level main() is capable of propagating exceptions to calling code instead of just dying with System.exit(). Being able to just build the same command-line argument array and run KeyTool.main(stuff) instead of having to mess with platform differences seems like a very Java-esque thing to do, right?
In practice, weird things happen and we don't know why.
We want to capture any output from running KeyTool, which starts off like this:
// jdk/src/share/classes/sun/security/tools/KeyTool.java, line 331:
public static void main(String[] args) throws Exception {
KeyTool kt = new KeyTool();
kt.run(args, System.out);
}
private void run(String[] args, PrintStream out) throws Exception {
// real code here, sends to 'out'
}
The KeyTool entry points don't allow us to pass a PrintStream, it's hardcoded to use System.out. That should be okay thanks to System.setOut. We have an OutputStream subclass which feeds to a JTextComponent, but for initial coding, redirecting to a text file is fine. So our code does
PrintStream orig = System.out;
try {
System.out.println("This is the last visible console line");
System.setOut(new PrintStream("redirect_test.txt"));
System.out.println("This is now redirected!");
KeyTool.main(keytool_argv); // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
System.setOut(orig);
System.out.println("Back to normal console output");
}
But when we run the code, the redirect_test.txt file contains only "This is now redirected!". The output from keytool's "-help" still shows up on the console, along with the before-and-after println calls.
There are some other oddities in calling KeyTool directly, like the package and class name has changed between Java 7 and Java 8, but that's easy to deal with via reflection. (The comments in the KeyTool source in Java 8 still refer to the Java 7 name, heh.) The only thing just freaky weird is how its "System.out" is strangely not affected by the same redirection that works everywhere else. (No, there are no weird import statements bringing in a special System replacement.)
Here's an online copy of Java 7's KeyTool.java if you don't happen to have OpenJDK sitting around.
You just need to redirect both System.out and System.err, since the usage instructions get printed to the standard error stream instead of the standard output stream. Try this:
PrintStream original = System.out;
PrintStream redirected = new PrintStream("redirect_test.txt")
try {
System.out.println("This is the last visible console line");
System.setOut(redirected);
System.setErr(redirected);
System.out.println("This is now redirected!");
KeyTool.main(keytool_argv); // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
System.setOut(original);
System.setErr(original);
System.out.println("Back to normal console output");
}

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);

Is it Possible to Use the Soot Analyses Without Calling soot.Main.main(...)?

I want to use Soot to do a static analysis of Java programs, including for example the control flow graph.
The various tutorials say that the "standard way" to use Soot is to create a main method where one adds custom transforms to the Soot pipeline and then call soot.Main.main(...):
public static void main(String[] args) {
PackManager.v().getPack("jtp").add(
new Transform("jtp.gotoinstrumenter", GotoInstrumenter.v()));
soot.Main.main(args);
}
Of course, this has some serious limitations if you want to use Soot in something else than a command line tool. For example, it is unclear to me whether it is even legal to call Soot's main method more than once in a program.
So does anyone know a possibility to use the Soot analysis tools directly through an API that is a bit more sophisticated?
The answer is yes. In your main you can set up the class that you working with:
configure("../yourClasspath/");
SootClass sootClass = Scene.v().loadClassAndSupport("className");
sootClass.setApplicationClass();
// Retrieve the method and its body
SootMethod m = c.getMethodByName("methodName");
Body b = m.retrieveActiveBody();
// Instruments bytecode
new YourTransform().transform(b);
After that, you might build the CFG and run some analysis.
It follows the configure method:
public static void configure(String classpath) {
Options.v().set_verbose(false);
Options.v().set_keep_line_number(true);
Options.v().set_src_prec(Options.src_prec_class);
Options.v().set_soot_classpath(classpath);
Options.v().set_prepend_classpath(true);
PhaseOptions.v().setPhaseOption("bb", "off");
PhaseOptions.v().setPhaseOption("tag.ln", "on");
PhaseOptions.v().setPhaseOption("jj.a", "on");
PhaseOptions.v().setPhaseOption("jj.ule", "on");
Options.v().set_whole_program(true);
}

java newbie question: richer java subprocesses

I would like to spawn a subprocess Java Virtual Machine (through a Java API call) and communicate with it.
Maybe I'm just not searching for the right thing on Google, but I keep getting pointed back to Runtime.exec(), which is not what I want. I know I could connect standard input/output and use various serialization techniques, or use remote method invocation, but these seem to cumbersome/heavyweight. It seems like an API call could be more robust than attempting to call Runtime.exec("/usr/bin/java -cp [system.getclasspath] ...").
The purpose / motivation of this is that dynamically reloading classes with many dependencies seems somewhat tricky (several sites mention reading a .class file and passing it to [ClassLoader].defineClass, but this doesn't handle loading of dependent class files well). If I don't need much communication bandwidth or low latency, I think a new JVM instance would work fine; it's that communicating with it seems cumbersome. In any case this question isn't high priority; I'll probably go the straightforward route and see how I can load dependent class files (e.g. ones for inner classes).
As long as the classes you want to load are in a JAR file or a directory tree separate from your main app, using an URLClassLoader and running them in a separate Thread works fine. AFAIK all Java app servers work like this, so it's definitely a proven technique.
I would definitely recommend taking a look at the class loader mechanism used by the Tomcat servlet container - they seem to have exactly the same class loading problem and seem to have solved it very well.
If you go the communication route you should consider Java RMI.
ClassRunner (shown in the listing below) uses ProcessBuilder to run the main function of any available class in your classpath using the same class path and library path as the current JVM. It will even clone the environment and the working directory of the current JVM.
Caveat: ClassRunner assumes that java is on the PATH of the current JVM. You may want to put some logic around locating java or java.exe based on System.getProperty("java.home") :)
Listing of ClassRunner.java:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ClassRunner
{
private final Class<?> classToRun;
public ClassRunner(Class<?> classToRun)
{
this.classToRun = classToRun;
}
public void run(String... args) throws Exception
{
String javaCommand = "java";
String libraryPath = "-Djava.library.path=\"" + System.getProperty("java.library.path") + "\"";
String classpath = "\"" + System.getProperty("java.class.path") + "\"";
ProcessBuilder processBuilder = new ProcessBuilder(javaCommand,
libraryPath,
"-classpath", classpath,
classToRun.getCanonicalName());
processBuilder.redirectErrorStream();
for (String arg : args) processBuilder.command().add(arg);
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) System.out.println(line);
reader.close();
process.waitFor();
}
public static void main(String[] args) throws Exception
{
new ClassRunner(Main.class).run("Hello");
}
}
Listing of Main.java:
public class Main
{
public static void main(String... args)
{
System.out.println("testing Main");
for (String arg : args) System.out.println(arg);
}
}

Categories

Resources