I Created a class JavaRunner that dynamically creates a file from string, compiles it in memory and runs it's main method (I also created a method that writes the file and compiles it on disk with similar results).
I created 2 other classes that call the runner.
The first is TerminalRunner which takes the class name and source as arguments and calls JavaRunner.compile, this works fine because it only runs once every time I call it.
The second class is RunnerServlet which starts a small java server that receives a post request compiles using JavaRunner and runs the code and returning a JSON object with the sys.out and sys.err streams.
if I post {name:"Main", code:"[Some Java code]"} I get the correct response; however if I call the same class Main with different source code I get the first result.
I traced the code and the source String is delivered correctly to the JavaCompiler.
The problem has to do with the compiled class, my guess it is somehow cached by the JVM.
This is The compile method in JavaRunner.java
public static void compile(String name, String code, int timeLimit){
/*Creating dynamic java source code file object*/
SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject (name, code) ;
JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject} ;
/*Instantiating the java compiler*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/**
* Retrieving the standard file manager from compiler object, which is used to provide
* basic building block for customizing how a compiler reads and writes to files.
*
* The same file manager can be reopened for another compiler task.
* Thus we reduce the overhead of scanning through file system and jar files each time
*/
StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, null, null);
try {
stdFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File("./temp")));
} catch (IOException e) {
e.printStackTrace();
}
/* Prepare a list of compilation units (java source code file objects) to input to compilation task*/
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);
/*Prepare any compilation options to be used during compilation*/
//In this example, we are asking the compiler to place the output files under bin folder.
List<String> compileOptions = new ArrayList<String>();
// compileOptions.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
// Iterable<String> compilationOptionss = Arrays.asList(compileOptions);
/*Create a diagnostic controller, which holds the compilation problems*/
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
/*Create a compilation task from compiler by passing in the required input objects prepared above*/
CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, compileOptions, null, compilationUnits) ;
//Perform the compilation by calling the call method on compilerTask object.
boolean status = compilerTask.call();
if (!status){//If compilation error occurs
/*Iterate through each compilation problem and print it*/
for (Diagnostic diagnostic : diagnostics.getDiagnostics()){
System.err.format("Error on line %d in %s", diagnostic.getLineNumber(), diagnostic);
}
} else {
// ExecutorService service = Executors.newSingleThreadExecutor();
// try {
// Runnable r = new Runnable() {
// #Override
// public void run() {
try {
Class.forName(name).getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null });
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
} catch (NoSuchMethodException e) {
System.err.println("No such method: " + e);
} catch (IllegalAccessException e) {
System.err.println("Illegal access: " + e);
} catch (InvocationTargetException e) {
System.err.println("RuntimeError: "+e.getTargetException());
}
// }
// };
// Future<?> f = service.submit(r);
// f.get(timeLimit, TimeUnit.MILLISECONDS); // attempt the task for timelimit default 5 seconds
// }
// catch (final InterruptedException e) {
// System.err.println("Thread Interrupted: " + e);
// }
// catch (final TimeoutException e) {
// System.err.println("TimeoutException: Your program ran for more than "+timeLimit);
// }
// catch (final ExecutionException e) {
// e.printStackTrace();
// }
// finally {
// service.shutdown();
// }
}
try {
(new File("./temp/"+name+".class")).delete();
stdFileManager.close() ;//Close the file manager
} catch (IOException e) {
e.printStackTrace();
}
}
This is the DynaDynamicJavaSourceCodeObject
class DynamicJavaSourceCodeObject extends SimpleJavaFileObject{
private String sourceCode ;
/**
* Converts the name to an URI, as that is the format expected by JavaFileObject
*
*
* #param String name given to the class file
* #param String source the source code string
*/
protected DynamicJavaSourceCodeObject(String name, String source) {
super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
this.sourceCode = source ;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException {
return sourceCode ;
}
public String getSourceCode() {
return sourceCode;
}
}
Any advice?
so far I set the CLASS_OUPUT to a /temp directory where I delete them
however once a class is defined even after I delete it it remains in memory somewhere
Is there a way to clear classes from java's memory?
I created a repo with my current progress here
My workaround, if all else fails,is to generate random file names then every 10000 compilation I would restart the server or something (but it's messy)
So thanks to the suggestions by #pm-77-1 and hot-licks in the comments
I used theSecureClassLoader class and made it so that the compiled bytecode is loaded there
here's the full class
public class JavaRunner {
public static void compile(String name, String code){
compile(name,code,5000);
}
/**
* compiles and runs main method from code
* #param name Class Name
* #param code String to compile
* #param timeLimit (otional) limit for code to run, default to 5 seconds
*/
public static void compile(String name, String code, int timeLimit){
/*Creating dynamic java source code file object*/
SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject (name, code) ;
JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject} ;
/*Instantiating the java compiler*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/**
* Retrieving the standard file manager from compiler object, which is used to provide
* basic building block for customizing how a compiler reads and writes to files.
*
* The same file manager can be reopened for another compiler task.
* Thus we reduce the overhead of scanning through file system and jar files each time
*/
StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, null, null);
//uses custom file manager with defined class loader inorder to unload the compiled class when this is done
ClassFileManager fileManager = new ClassFileManager(stdFileManager);
/* Prepare a list of compilation units (java source code file objects) to input to compilation task*/
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);
/*Prepare any compilation options to be used during compilation*/
//In this example, we are asking the compiler to place the output files under bin folder.
List<String> compileOptions = new ArrayList<String>();
// compileOptions.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
// Iterable<String> compilationOptionss = Arrays.asList(compileOptions);
/*Create a diagnostic controller, which holds the compilation problems*/
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
/*Create a compilation task from compiler by passing in the required input objects prepared above*/
CompilationTask compilerTask = compiler.getTask(null, fileManager, diagnostics, compileOptions, null, compilationUnits) ;
//Perform the compilation by calling the call method on compilerTask object.
boolean status = compilerTask.call();
if (!status){//If compilation error occurs
/*Iterate through each compilation problem and print it*/
for (Diagnostic diagnostic : diagnostics.getDiagnostics()){
System.err.format("Error on line %d in %s", diagnostic.getLineNumber(), diagnostic);
}
} else {
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Runnable r = new Runnable() {
#Override
public void run() {
try {
fileManager.getClassLoader(null).loadClass(name).getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null });
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
} catch (NoSuchMethodException e) {
System.err.println("No such method: " + e);
} catch (IllegalAccessException e) {
System.err.println("Illegal access: " + e);
} catch (InvocationTargetException e) {
System.err.println("RuntimeError: "+e.getTargetException());
}
try {
fileObject.delete();
fileManager.close();
ResourceBundle.clearCache(ClassLoader.getSystemClassLoader()); // <--useless
} catch (IOException e) {
e.printStackTrace();
}
}
};
Future<?> f = service.submit(r);
f.get(timeLimit, TimeUnit.MILLISECONDS);
}
catch (final InterruptedException e) {
System.err.println("Thread Interrupted: " + e);
}
catch (final TimeoutException e) {
System.err.println("TimeoutException: Your program ran for more than "+timeLimit);
}
catch (final ExecutionException e) {
e.printStackTrace();
}
finally {
service.shutdown();
}
}
}
}
This prepares a dynamic java source code for compilation.
class DynamicJavaSourceCodeObject extends SimpleJavaFileObject{
private String sourceCode ;
/**
* Converts the name to an URI, as that is the format expected by JavaFileObject
*
*
* #param String name given to the class file
* #param String source the source code string
*/
protected DynamicJavaSourceCodeObject(String name, String source) {
super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
this.sourceCode = source ;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException {
return sourceCode ;
}
public String getSourceCode() {
return sourceCode;
}
}
the idea is to create a Dynamic class instead of writing to file
class JavaClassObject extends SimpleJavaFileObject {
/**
* Byte code created by the compiler will be stored in this
* ByteArrayOutputStream so that we can later get the
* byte array out of it
* and put it in the memory as an instance of our class.
*/
protected ByteArrayOutputStream bos =
new ByteArrayOutputStream();
/**
* Registers the compiled class object under URI
* containing the class full name
*
* #param name
* Full name of the compiled class
* #param kind
* Kind of the data. It will be CLASS in our case
*/
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/')
+ kind.extension), kind);
}
/**
* Will be used by our file manager to get the byte code that
* can be put into memory to instantiate our class
*
* #return compiled byte code
*/
public byte[] getBytes() {
return bos.toByteArray();
}
/**
* Will provide the compiler with an output stream that leads
* to our byte array. This way the compiler will write everything
* into the byte array that we will instantiate later
*/
#Override
public OutputStream openOutputStream() throws IOException {
return bos;
}
}
We use this file manager so that the compiled class from source can be unloaded also not having to write to file System
class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
/**
* Instance of JavaClassObject that will store the
* compiled bytecode of our class
*/
private JavaClassObject jclassObject;
/**
* Instance of ClassLoader
*/
private SecureClassLoader classLoader;
/**
* Will initialize the manager with the specified
* standard java file manager
*
* #param standardManger
*/
public ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
this.classLoader = new SecureClassLoader() {
#Override
protected Class<?> findClass(String name)
throws ClassNotFoundException {
byte[] b = jclassObject.getBytes();
return super.defineClass(name, jclassObject
.getBytes(), 0, b.length);
}
};
}
/**
* Will be used by us to get the class loader for our
* compiled class. It creates an anonymous class
* extending the SecureClassLoader which uses the
* byte code created by the compiler and stored in
* the JavaClassObject, and returns the Class for it
*/
#Override
public ClassLoader getClassLoader(Location location) {
return this.classLoader;
}
public void unloadClass(Location location) {
this.classLoader = null;
this.jclassObject = null;
System.gc();
}
/**
* Gives the compiler an instance of the JavaClassObject
* so that the compiler can write the byte code into it.
*/
#Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, Kind kind, FileObject sibling)
throws IOException {
jclassObject = new JavaClassObject(className, kind);
return jclassObject;
}
}
I think the only way for a Class<?> object to be garbage collected, is for the associated ClassLoader to be garbage collected, and this only becomes eligible for collection if there are no more references to the ClassLoader and any of the classes loaded through this ClassLoader. Have a look at this question for more information.
Related
Hey I am trying to use Google Cloud Speech API into Android but I am getting this error and am unable to get around it.
java.lang.VerifyError: Verifier rejected class com.google.cloud.speech.v1
.StreamingRecognizeResponse due to bad method java.lang.Object com.google
.cloud.speech.v1.StreamingRecognizeResponse.dynamicMethod(com.google.protobuf
.GeneratedMessageLite$MethodToInvoke, java.lang.Object, java.lang.Object)
(declaration of 'com.google.cloud.speech.v1.StreamingRecognizeResponse'
appears in /data/app/com.curieo.podcast-1/base.apk:classes65.dex)
at com.google.cloud.speech.v1.StreamingRecognizeResponse.getDefaultInstance(StreamingRecognizeResponse.java:1095)
at com.google.cloud.speech.v1.SpeechGrpc.<clinit>(SpeechGrpc.java:67)
at com.curieo.podcast.ui.fragment.dates.googlecloud.StreamingRecognizeClient.<init>(StreamingRecognizeClient.java:57)
at com.curieo.podcast.ui.fragment.dates.googlecloud.MicrophoneStreamRecognizeClient.<init>(MicrophoneStreamRecognizeClient.java:54)
at com.curieo.podcast.ui.fragment.dates.RecordFragment$1.run(RecordFragment.java:112)
I searched for this but couldn't find a solution. here is the code where error occurs
private Thread runner = new Thread() {
public void run() {
try {
MicrophoneStreamRecognizeClient client;
synchronized (this) {
try {
client = new MicrophoneStreamRecognizeClient(getResources().openRawResource(R.raw.credential), Self); //crashes here
client.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
Here is the code snippet of MicrophoneStreamRecognizeClient class:
public class MicrophoneStreamRecognizeClient {
private String host = "speech.googleapis.com";
private Integer port = 443;
private ManagedChannel channel;
private StreamingRecognizeClient client;
private final List<String> OAUTH2_SCOPES = Arrays.asList("https://www.googleapis.com/auth/cloud-platform");
/**
*
* #param authorizationFile
* #param host
* #param port
* #return
* #throws IOException
*/
private ManagedChannel createChannel(InputStream authorizationFile, String host, int port) throws IOException {
GoogleCredentials creds = GoogleCredentials.fromStream(authorizationFile);
creds = creds.createScoped(OAUTH2_SCOPES);
return ManagedChannelBuilder.forAddress(host, port)
.intercept(new ClientAuthInterceptor(creds, Executors.newSingleThreadExecutor()))
.build();
}
/**
*
* #param autorizationFile
* #throws IOException
*/
public MicrophoneStreamRecognizeClient(InputStream autorizationFile, IResults screen) throws IOException {
channel = createChannel(autorizationFile, host, port);
client = new StreamingRecognizeClient(channel, screen);
}
/**
*
* #throws IOException
* #throws InterruptedException
*/
public void start() throws IOException, InterruptedException {
client.recognize();
}
/**
*
* #throws InterruptedException
*/
public void stop() throws InterruptedException {
client.shutdown();
}
}
Cleaning out the build folder resolved the problem. Not sure why ART had an issue but Dalvik did not.
Running a gradle clean task was not clearing out my build folder all the way. I had to do it manually, but clean may work for some people.
Reference
java.lang.VerifyError can happen for some reason:
A class tries to extend a class declared as final
A method tries to override a super method that is declared as final
A wrong argument is passed to a method
clint = new MicrophoneStreamRecognizeClient(getResources()
.openRawResource(R.raw.credential), Self); //crashes here
I don't know what is Self. Is that ok?
Try this.
synchronized (this) {
MicrophoneStreamRecognizeClient client;
try {
client = new MicrophoneStreamRecognizeClient(getResources().openRawResource(R.raw.credential), Self); //crashes here
client.start();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Because another issue can be !! Synchronized block inside try / catch block !! cause of java.lang.VerifyError reference
If the java.lang.VerifyError is triggered because library differences between runtime and compiling, as #Enamul also suggested, then you might wanna check differences between com.google.cloud.speech.v1.StreamingRecognizeResponse and com.google.cloud.speech.v1beta1.StreamingRecognizeResponse.
Even so, try with the beta, newer, version of the library. The example that inspired you uses the beta version.
I have a JavaFX app where there is an editor. In the editor, the user will be able to write java code and I have a button to compile this code and run the main method. For example the editor will contain this code:
public class Test {
public static void main(String[] args) {
System.out.println("hello");
}
}
The button on click, will run this code:
runButton.setOnAction(e -> {
compiler.set(editor.getText());
try {
compiler.createFile();
} catch (IOException e1) {
e1.printStackTrace();
}
compiler.compile();
compiler.run();
});
In the compiler class, there is the following implementation:
public class CodeCompiler {
public String className;
public String code;
public void set(String code) {
try {
this.className = code.substring(code.indexOf(" class ") + 6, code.indexOf(" {")).trim();
} catch(StringIndexOutOfBoundsException e) {
}
this.code = code;
}
public void createFile() throws IOException {
PrintWriter pw = new PrintWriter("speech2code/src/main/java/" + className + ".java");
pw.close();
FileWriter writer = new FileWriter("speech2code/src/main/java/" + className + ".java", true);
writer.write(code);
writer.flush();
writer.close();
}
public void compile() {
File file = new File("speech2code/src/main/java/" + className + ".java");
File classFile = new File("speech2code/src/main/java/" + className + ".class");
classFile.delete(); // delete class file f it exists
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, file.getPath());
}
public void run() {
Class<?> cls = null;
try {
cls = Class.forName(className);
System.out.println(cls == null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method meth = null;
try {
meth = cls.getMethod("main", String[].class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
String[] params = null;
try {
meth.invoke(null, (Object) params);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
Now the code above successfully creates the java file, class file and runs correctly the first time. Now when I change the editor code to print something else, it outputs the result of the first time the code was running. So, in this case, it will still print 'hello' instead of whatever it's current value.
Any problem what might be wrong?
Thanks!
You need to create a new classloader for the new class. The class does not get reloaded just because you compiled it.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {classFile});
Then you can ask this loader for the class:
Class<?> cls = Class.forName(className, true, classLoader);
I'm trying to build a client-server IDE, and I'm having problems with getting the error log/exception log of runtime exceptions from the in-memory compiled/run class.
My code looks like this:
public class ServerThread implements Runnable {
private ObjectInputStream objectInputStream;
private ObjectOutputStream objectOutputStream;
private ByteArrayOutputStream byteArrayOutputStream;
private PrintStream out;
public ServerThread(Socket socket) {
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
byteArrayOutputStream = new ByteArrayOutputStream();
out = new PrintStream(byteArrayOutputStream);
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public void run() {
try {
StringBuffer sourceCode = new StringBuffer();
sourceCode.append(objectInputStream.readObject());
Class<?> myClass = MyJavaCompiler.compile("Test", sourceCode.toString());
Method mainMethod = myClass != null ? myClass.getDeclaredMethod("main", String[].class) : null;
Object myObject = myClass.getConstructor().newInstance();
try {
System.setOut(out);
System.out.print(mainMethod.invoke(myObject, new Object[]{null}));
objectOutputStream.writeObject(byteArrayOutputStream.toString());
} catch (InvocationTargetException e) {
Class<?>[] exceptionsList = mainMethod.getExceptionTypes();
for(Class<?> c : exceptionsList){
objectOutputStream.writeObject(c.getName());
}
}
} catch (Exception e) {}
}
}
and this is the compiler:
import javax.tools.*;
import java.util.Arrays;
/**
* Created by alin on 8/23/15.
*/
public class MyJavaCompiler {
private static JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
public static Class<?> compile(String className, String sourceCodeInText) throws Exception {
JavaSourceFromString sourceCode = new JavaSourceFromString(className, sourceCodeInText);
CompiledCode compiledCode = new CompiledCode(className);
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceCode);
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(ClassLoader.getSystemClassLoader());
MyJavaFileManager fileManager = new MyJavaFileManager(javac.getStandardFileManager(null, null, null), compiledCode, dynamicClassLoader);
JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, null, null, null, compilationUnits);
boolean result = task.call();
if(result){
System.out.println("SUCCESS");
return dynamicClassLoader.loadClass(className);
}
else{
System.out.println("FAIL");
return null;
}
}
}
At this moment, when I have, for example NullPointerException in my in-memory class, I get "[]" sent to client.
EDIT:
Thanks Holger for your answer and feedback. I managed to get the code to do what i wanted by simply adding e.printStackTrace(PrintStream), code below:
try {
System.setOut(out);
System.out.print(mainMethod.invoke(myObject, new Object[]{null}));
objectOutputStream.writeObject(byteArrayOutputStream.toString());
} catch (InvocationTargetException e) {
e.printStackTrace(out);
objectOutputStream.writeObject(byteArrayOutputStream.toString());
}
The idea was to redirect my error output from my in-memory class to client from server.
You should get used to a consistent behavior regarding null treatment. Here
Class<?> myClass = MyJavaCompiler.compile("Test", sourceCode.toString());
Method mainMethod = myClass != null ? myClass.getDeclaredMethod("main", String[].class) : null;
Object myObject = myClass.getConstructor().newInstance();
You are considering that myClass could be null when initializing mainMethod but right in the next line you are unconditionally invoking getConstructor() on myClass which would lead to a NullPointerException when myClass is null. Even when that didn’t fail, you have assigned null to mainMethod in case myClass is null, but are going to call invoke, again without checking for null.
Nevertheless, when your compile method returns a non-null value, you will proceed, but in the case you receive an InvocationTargetException, you are not going to send anything related to that exception, in fact you are not accessing e at all, but instead you are sending the result of mainMethod.getExceptionTypes() which are the types which that method declares via throws clause. That list might indeed be empty, the main method doesn’t need to declare any exceptions and may still throw RuntimeExceptions or Errors.
You are also performing myClass.getConstructor().newInstance(); outside that try … catch clause and any throwable thrown by this constructor will get handled by the outer try … catch in the worst way ever possible, catch (Exception e) {} …
I am using Java reflection to invoke a method in a remote server from a local application. As part of the request to invoke the method, I am also sending the state of the Class object, which contains the value of the variables that are modified by the method during runtime, such that when the method is invoked remotely and the application gets the result from the server, the variables which were modified can be updated in my local application.
Client
public MyClass {
double var1;
...
public long dotest(){
Method toExecute;
Class<?>[] paramTypes = null;
Object[] paramValues = null;
Long result = null;
try{
/*
* Method specifications are stored and sent in a Class called MyPack to the server
*/
toExecute = this.getClass().getDeclaredMethod("localdotest", paramTypes);
Vector results = getCloudController().execute(toExecute,paramValues,this,this.getClass());
if(results != null){
result = (Long)results.get(0);
copyState(results.get(1));
}else{
result = localdotest();
}
} catch (SecurityException se){
} catch (NoSuchMethodException ns){
} catch (Throwable th){
}
return result;
}
public void copyState(Object state){
MyClass
localstate = (MyClass) state;
this.var1 = localstate.var1;
}
Server
myPack = (Pack) ois.readObject();
functionName = myPack.getfunctionName();
paramTypes = myPack.getparamTypes();
paramValues = myPack.getparamValues();
state = myPack.getstate();
stateDType = myPack.getstateType();
try {
Class cls = Class.forName(stateDType.getName());
Method method = cls.getDeclaredMethod(functionName, paramTypes);
try{
Object result = method.invoke(state, paramValues);
ResultPack rp = new ResultPack(result, state);
oos.writeObject(rp);
oos.flush();
} catch (IllegalAccessException ex) {
returnnull(oos);
} catch (InvocationTargetException ex) {
returnnull(oos);
} catch(Exception ex){
ResultPack rp = new ResultPack(null, state);
oos.writeObject(rp);
oos.flush();
}
However, I have notice that the state of the Class sent is always the same when the method is invoked remotely. So, I am planning to cache the state of the class in the server. The goal is to avoid sending the same state every time the method is invoked.
So far, I don't see any problem to do so. I tried to find something related, and these links 1, 2 are the most closer topics I found, but still I cannot get off this "itch" from my mind. Could somebody please point me out if there is any potential drawback to adopt such strategy. Thanks in advance.
My QA team is doing business lifecycle testing (i.e. aging, expiring, due, past due etc) , that requires application clock to be moved. I can change all my code to refer to a adjusted clock (that I control). The issues is the (web) applications uses several 3rd party tools (e.g. Spring Batch, Activiti etc.) that relies on current time and uses System.currentTimeMillis() directly or indirectly through Date or Calendar.
Option 1 - Spring AOP. When I tried this option it seemed it only instruments Spring loaded beans only (?) Since System class was loaded outside of Spring framework it could not instrument it.
Option 2 - JMockit. Somewhat unconventional to have JMockit jar past JUnit.
Option 3 - Use Java 6 instrumentation (common piece between Option 1 and Option 2). Back to the basics... (find the relevant code below).
However, the assert in the test code always fails.
I have hit a roadblock with all the three options. Can't believe no one have done this before, but can't find an reasonable solution either.
Thanks in advance.
public class InstrumentationAgent {
private static Instrumentation instrumentation = null;
/**
* JVM hook to dynamically load InstrumentationAgent at runtime.
*
* The agent class may have an agentmain method for use when the agent is
* started after VM startup.
*
* #param agentArgument
* #param instrumentation
*/
public static void agentmain(String agentArgument, Instrumentation instrumentation) {
InstrumentationAgent.instrumentation = instrumentation;
}
/**
* Programmatic hook to dynamically load modified byte codes. This method initializes/load the agent if necessary.
*
* #param definitions
* #throws Exception
*/
public static void redefineClasses(ClassDefinition... definitions) throws Exception {
if (InstrumentationAgent.instrumentation == null) {
loadAgent();
}
InstrumentationAgent.instrumentation.redefineClasses(definitions);
}
private synchronized static void loadAgent() throws Exception {
if (InstrumentationAgent.instrumentation != null) {
return;
}
// Build the agent.jar file
final File jarFile = File.createTempFile("agent", ".jar");
jarFile.deleteOnExit();
final Manifest manifest = new Manifest();
final Attributes mainAttributes = manifest.getMainAttributes();
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
mainAttributes.put(new Attributes.Name("Agent-Class"), InstrumentationAgent.class.getName());
mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), "true");
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
final JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), manifest);
final JarEntry agent = new JarEntry(InstrumentationAgent.class.getName().replace('.', '/') + ".class");
jos.putNextEntry(agent);
final ClassPool pool = ClassPool.getDefault();
final CtClass ctClass = pool.get(InstrumentationAgent.class.getName());
jos.write(ctClass.toBytecode());
jos.closeEntry();
jos.close();
// Attach to VM and load the agent
VirtualMachine vm = VirtualMachine.attach(getPidFromRuntimeMBean());
vm.loadAgent(jarFile.getAbsolutePath());
vm.detach();
}
private static String getPidFromRuntimeMBean() throws Exception {
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
Field jvmField = mxbean.getClass().getDeclaredField("jvm");
jvmField.setAccessible(true);
VMManagement management = (VMManagement) jvmField.get(mxbean);
Method method = management.getClass().getDeclaredMethod("getProcessId");
method.setAccessible(true);
Integer processId = (Integer) method.invoke(management);
return processId.toString();
}
}
public class SystemTimeInstrumentation {
private static long timeAdjustment = 200000L;
private static byte[] originalClassByteArray;
public static void startAdjustedClock() {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = null;
byte[] instrumentedClassByteArray = null;
try {
originalClassByteArray = pool.get(System.class.getName()).toBytecode();
ctClass = pool.makeClass(new java.io.ByteArrayInputStream(originalClassByteArray), false);
CtMethod ctMethod = ctClass.getDeclaredMethod("currentTimeMillis");
ctMethod.setBody("return 0L;");
instrumentedClassByteArray = ctClass.toBytecode();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CannotCompileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (ctClass != null) {
ctClass.detach();
}
}
try {
InstrumentationAgent.redefineClasses(new ClassDefinition[] { new ClassDefinition(System.class,
instrumentedClassByteArray) });
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void stopAdjustedClock() {
if (originalClassByteArray == null) {
throw new RuntimeException("The stopAdjustedClock() called before startAdjustedClock()");
} else {
try {
InstrumentationAgent.redefineClasses(new ClassDefinition[] { new ClassDefinition(System.class,
originalClassByteArray) });
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
originalClassByteArray = null;
}
}
public class SystemTimeInstrumentationTest extends TestCase {
#Test
public void testModifiedClock() throws Exception {
long unmodifiedTime = System.currentTimeMillis();
SystemTimeInstrumentation.startAdjustedClock();
long modifiedTime = System.currentTimeMillis();
SystemTimeInstrumentation.stopAdjustedClock();
assertTrue("The difference should me more than 200000", (modifiedTime-unmodifiedTime)>200000L);
}
}