How can I log the parameters passed to a method at runtime ? Is there any Java library for this or any any exception that can be raised to monitor it ?
You can use javassist's ProxyFactory or Translator to change to print the arguments at runtime:
Using Translator (with a new ClassLoader):
public static class PrintArgumentsTranslator implements Translator {
public void start(ClassPool pool) {}
#Override
public void onLoad(ClassPool pool, String cname)
throws NotFoundException, CannotCompileException {
CtClass c = pool.get(cname);
for (CtMethod m : c.getDeclaredMethods())
insertLogStatement(c, m);
for (CtConstructor m : c.getConstructors())
insertLogStatement(c, m);
}
private void insertLogStatement(CtClass c, CtBehavior m) {
try {
List<String> args = new LinkedList<String>();
for (int i = 0; i < m.getParameterTypes().length; i++)
args.add("$" + (i + 1));
String toPrint =
"\"----- calling: "+c.getName() +"." + m.getName()
+ args.toString()
.replace("[", "(\" + ")
.replace(",", " + \", \" + ")
.replace("]", "+\")\"");
m.insertBefore("System.out.println("+toPrint+");");
} catch (Exception e) {
// ignore any exception (we cannot insert log statement)
}
}
}
*Note that you need to change the default ClassLoader so that you can instrument the classes, so before calling your main you need some inserted the following code:
public static void main(String[] args) throws Throwable {
ClassPool cp = ClassPool.getDefault();
Loader cl = new Loader(cp);
cl.addTranslator(cp, new PrintArgumentsTranslator());
cl.run("test.Test$MyApp", args); // or whatever class you want to start with
}
public class MyApp {
public MyApp() {
System.out.println("Inside: MyApp constructor");
}
public static void main(String[] args) {
System.out.println("Inside: main method");
new MyApp().method("Hello World!", 4711);
}
public void method(String string, int i) {
System.out.println("Inside: MyApp method");
}
}
Outputs:
----- calling: test.Test$MyApp.main([Ljava.lang.String;#145e044)
Inside: main method
----- calling: test.Test$MyApp.Test$MyApp()
Inside: MyApp constructor
----- calling: test.Test$MyApp.method(Hello World!, 4711)
Inside: MyApp method
Using ProxyFactory
public class Test {
public String method(String string, int integer) {
return String.format("%s %d", string, integer);
}
public static void main(String[] args) throws Exception {
ProxyFactory f = new ProxyFactory();
f.setSuperclass(Test.class);
Class<?> c = f.createClass();
MethodHandler mi = new MethodHandler() {
public Object invoke(
Object self, Method m, Method proceed, Object[] args)
throws Throwable {
System.out.printf("Method %s called with %s%n",
m.getName(), Arrays.toString(args));
// call the original method
return proceed.invoke(self, args);
}
};
Test foo = (Test) c.newInstance();
((Proxy) foo).setHandler(mi);
foo.method("Hello", 4711);
}
}
Output:
Method method called with [Hello, 4711]
You should try to use AOP. Here is an example that does more or less what you want: How to use AOP with AspectJ for logging?
I think you can register your MBean then only you will be able to check using JMX.
Link: http://docs.oracle.com/cd/E19159-01/819-7758/gcitp/index.html
Related
So I have two threads, Thread A and B. Thread A contains an ArrayList that has my objects. What I want to do is that: on Thread B, creating new object and adding it to that list. Then changing that object's field on Thread A. After that, read that changed field on Thread B. But sadly, when I read it on Thread B, I get the default value that created on object constructor. So, how can I read new that value?
UPDATE
Some info: onEnable method executed on application start by the API I use called "Bukkit", Everything bound to Bukkit executed on Thread A which is Bukkit's main thread
Main.class:
private static ExecutorService executorService;
private static List<Confirmation> confirmations;
public void onEnable() {
executorService = Executors.newCachedThreadPool();
confirmations = new ArrayList<>();
}
public static ExecutorService getExecutorService() {
return executorService;
}
public static Confirmation createConfirmation(CommandSender sender, String command, String[] args) {
removeConfirmation(sender, command);
Confirmation confirmation = new Confirmation(sender, command, args);
confirmations.add(confirmation);
return confirmation;
}
public static void removeConfirmation(CommandSender commandSender, String command) {
confirmations.removeIf(confirmation -> confirmation.getSender().getName().equals(commandSender.getName()) && confirmation.getCommand().equalsIgnoreCase(command));
}
public static Confirmation getConfirmation(CommandSender commandSender, String command) {
return confirmations.stream().filter(confirmation -> confirmation.getSender().getName().equals(commandSender.getName()) && confirmation.getCommand().equalsIgnoreCase(command)).findFirst().orElse(null);
}
public static Confirmation createConfirmationIfNull(CommandSender commandSender, String command, String[] args) {
Confirmation confirmation = getConfirmation(commandSender, command);
return confirmation != null ? confirmation : createConfirmation(commandSender, command, args);
}
Confirmation.class:
private final CommandSender sender;
private final String command;
private final String[] args;
private boolean confirmed;
public Confirmation(CommandSender sender, String command, String[] args) {
this.sender = sender;
this.command = command;
this.args = args;
this.confirmed = false;
}
public CommandSender getSender() {
return sender;
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public boolean isConfirmed() {
return confirmed;
}
public void setConfirmed(boolean bln) {
this.confirmed = bln;
}
Command.class:
#CommandHandler(name = "rank", usage = "/rank <set|prefix>", requiredRank = Rank.ADMINISTRATOR)
public void rank(CommandSender sender, Command command, String s, String[] args) { //Always called on Thread A
String str = args[0];
RankCommandAction action = Arrays.stream(RankCommandAction.values()).filter(rankCommandAction -> rankCommandAction.getName().equalsIgnoreCase(str)).findFirst().orElse(null);
if (action == null) {
sender.sendMessage(ChatColor.RED + "Usage: /rank <set|prefix>");
return;
}
Main.getExecutorService().submit(() -> action.getConsumer().accept(sender, args)); //Thread B
}
private enum RankCommandAction {
SET_PREFIX("prefix", (CommandSender sender, String[] args) -> {
CommandConfirmationEvent event = new CommandConfirmationEvent(sender, "rank", args, ChatColor.RED + "Are you sure?");
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sender.sendMessage("Prefix changed");
}
}),
SET_RANK("set", (CommandSender sender, String[] args) -> {
CommandConfirmationEvent event = new CommandConfirmationEvent(sender, "rank", args, ChatColor.RED + "Are you sure?");
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sender.sendMessage("Changed rank");
}
});
CommandConfirmationEvent.class:
private final CommandSender commandSender;
private final String command;
private final String[] args;
private final Confirmation confirmation;
private String warningMessage;
public CommandConfirmationEvent(CommandSender commandSender, String command, String[] args, String warningMessage) {
this.commandSender = commandSender;
this.command = command;
this.args = args;
this.warningMessage = warningMessage;
this.confirmation = Main.createConfirmationIfNull(commandSender, command, args);
}
public CommandSender getCommandSender() {
return commandSender;
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public Confirmation getConfirmation() {
return confirmation;
}
public String getWarningMessage() {
return warningMessage;
}
public void setWarningMessage(String warningMessage) {
this.warningMessage = warningMessage;
}
#Override
public boolean isCancelled() {
return this.confirmation != null && !this.confirmation.isConfirmed();
}
This is what literally executed when calling the event:
if (!event.getConfirmation().isConfirmed()) {
event.getCommandSender().sendMessage("Please confirm");
return;
}
Main.removeConfirmation(event.getConfirmation());
And lastly, this is the command to confirm (on Thread A):
#CommandHandler(name = "confirm")
public void confirm(CommandSender sender, Command cmd, String str, String[] args) { //Always called on Thread A
Confirmation confirmation = findConfirmation(sender, "confirm", args);
if (confirmation != null) {
confirmation.setConfirmed(true);
String[] arg = confirmation.getArgs();
Bukkit.dispatchCommand(sender, confirmation.getCommand() + (arg != null && arg.length > 0 ? " " + StringUtils.merge(arg, " ") : "")); //This runs the "rank" command again
Main.removeConfirmation(confirmation);
}
}
private Confirmation findConfirmation(CommandSender sender, String[] args) {
List<Confirmation> list = Main.getConfirmations(sender);
if (list.isEmpty())
return null;
Confirmation confirmation;
if (list.size() > 1) {
if (args.length == 0) {
sender.sendMessage(ChatColor.RED + "Since you have multiple confirmations awaiting, you must specify the confirmation you want to confirm.");
sender.sendMessage(ChatColor.RED + "Usage: /confirm <command>");
sender.sendMessage(ChatColor.RED + "Awaiting confirmations: " + StringUtils.merge(list.stream().map(Confirmation::getCommand).toArray(String[]::new), ", ") + ".");
return null;
} else {
Confirmation foundConfirmation = list.stream().filter(confirmation1 -> confirmation1.getCommand().equalsIgnoreCase(args[0])).findFirst().orElse(null);
if (foundConfirmation == null) {
sender.sendMessage(ChatColor.RED + "You don't have any confirmations for the command '" + args[0] + "'.");
return null;
} else {
confirmation = foundConfirmation;
}
}
} else {
confirmation = list.get(0);
}
return confirmation;
}
Issues
As per the given code, it can likely throw IndexOutOfBounds exception at this line objects.get(0).setName("TEST");
Reason
The task submitted to executor service will run on its own timeline (inaddition to sleep 1000)
Main thread sleeping for 200 is not going to help (even increasing to 2000 may not help deterministically)
What should be done
Create two CountDown Latches
Pass one latch to the submitted task
after setting it, it will count down on the latch and will wait on other latch
the main will wait on the latch that will be count down by task
main thread, once notified from task thread, will do the update and countdown on other latch
Now task will be notified and will print the updated value
static class MyObject {
volatile String value;
MyObject(String value) {
this.value = value;
}
}
public static void main(String[] args) {
final List<MyObject> objects = new ArrayList<>();
ExecutorService executorService = Executors.newSingleThreadExecutor();
CountDownLatch updateLatch = new CountDownLatch(1);
CountDownLatch addLatch = new CountDownLatch(1);
executorService.submit(() -> {
objects.add(new MyObject("NAME"));
try {
addLatch.countDown();
updateLatch.await();
System.out.println(objects.get(0).value); //This prints "NAME"
} catch (Exception e) {
e.printStackTrace();
}
});
try {
addLatch.await();
objects.get(0).value = "TEST";
updateLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
I found the problem: I was removing the object from the list on confirm command, and it removes the object before running the "rank" command again. That causes the second "rank" command execution to not find the confirmation object.
You should use the same object instance on both threads, and syncronize them when accessing this object.
There is no meaning in your description "Thread A contains an Arraylist". If it's a local variable, Thread B can never access it.
I have 3 Classes: Regulate, Luminosity, Test
From the class Regulate, I which to setting an attribute in the class Luminosity by invoking the method setAttribute
Then in class Test, I calling the method getAttribute.
The problem is, When I calling the method getAttribute, I find a different value that I set it.
This is the Class Luminosity
public class Luminosity{
public static int attribute;
public static int getAttribute(){
return attribute;
}
public static void setAttribute(int v) {
attribute=v;
try {
File fichier = new File("../../WorkspaceSCA/Lamp/value.txt");
PrintWriter pw = new PrintWriter(new FileWriter(fichier)) ;
String ch=Integer.toString(attribute);
pw.append(ch);
pw.println();
pw.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
the Regulate Code:
public class Regulate {
public static void main(String[] args) throws InterruptedException {
Luminosity.setSensedValue(50));
System.out.println("Value of Luminosity= "+ Luminosity.getSensedValue());
}
}
this shows me: Value of Luminosity= 50
Now, I want to recover this value from a different class(Test), like this:
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println("Value = "+ Luminosity.getSensedValue());
this shows me: Value= 0
I want to recover the same value.
Thank's in advance
You are start two different classes in two different threads.
Of course Luminosity doesn't have previous value, it was setting in different JVM.
If you want to setup an attribute and transfer it between two threads you can place it in a text file.
public class Luminosity {
private static final String FILE_NAME = "attribute.txt";
private int attribute;
public void writeAttribute(int val) throws IOException {
try (FileWriter fileWriter = new FileWriter(FILE_NAME)) {
fileWriter.append("" + val);
fileWriter.flush();
}
attribute = val;
}
public int readAttribute() throws IOException {
StringBuilder sb = new StringBuilder();
try (FileReader fileReader = new FileReader(FILE_NAME)) {
int r;
while (true) {
char[] buffer = new char[100];
r = fileReader.read(buffer);
if (r == -1) break;
sb.append(new String(Arrays.copyOf(buffer, r)));
}
} catch (FileNotFoundException e) {
return 0;
}
if (sb.length() == 0) return 0;
return Integer.parseInt(sb.toString());
}
public static void main(String[] args) throws IOException {
Luminosity luminosity = new Luminosity();
System.out.println("attribute after start: " + luminosity.readAttribute());
luminosity.writeAttribute(50);
System.out.println("new attribute: " + luminosity.readAttribute());
}
}
I have many objects with different metrics. I am building a formula based on the user input.
class Object{
double metrics1;
double metrics2;
.. double metricsN; //number of metrics is knowned
}
Users can input
formula=metrics1
or
formula=(metrics1+metrics2)/metrics3
I already have a parser to parse the formula, but I do not know how to store this expression for further calculation.
I want to avoid parsing the formula again and again for every single object (I can have up to a few hundred thousands).
Use ScriptEngine and reflection like this.
static class Evaluator {
ScriptEngine engine = new ScriptEngineManager()
.getEngineByExtension("js");
void formula(String formula) {
try {
engine.eval("function foo() { return " + formula + "; }");
} catch (ScriptException e) {
e.printStackTrace();
}
}
Object eval(Object values)
throws ScriptException,
IllegalArgumentException,
IllegalAccessException {
for (Field f : values.getClass().getFields())
engine.put(f.getName(), f.get(values));
return engine.eval("foo()");
}
}
public static class Object1 {
public double metrics1;
public double metrics2;
Object1(double metrics1, double metrics2) {
this.metrics1 = metrics1;
this.metrics2 = metrics2;
}
}
public static void main(String[] args)
throws ScriptException,
IllegalArgumentException,
IllegalAccessException {
Evaluator e = new Evaluator();
e.formula("metrics1 + metrics2");
Object1 object = new Object1(1.0, 2.0);
System.out.println(e.eval(object));
// -> 3.0
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
Objective:
Run a class
Change a second class
Save and Compile second class
Without stopping and starting first class the changes to second class should be visible in the console
Problem:
Currently the changes do not reflect after saving and compiling.
I think Joachim's code fragment works perfectly fine (not tested):
public class Autosaver implements Runnable {
public static void main(String args[]) {
Autosaver instance = new Autosaver();
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(instance, 0, 5, TimeUnit.SECONDS);
}
#Override
public void run() {
try {
Class<? extends Test> Test_class = reloadClass(Test.class.getProtectionDomain().getCodeSource().getLocation(), Test.class.getName());
new Autorunner(Test_class, new File("Test.txt")).run();
} catch (Exception e) {
e.printStackTrace();
}
}
private static <X> Class<X> reloadClass(URL classLocation, String className) throws ClassNotFoundException, IOException {
URLClassLoader loader = new URLClassLoader(new URL[] { classLocation }, String.class.getClassLoader());
#SuppressWarnings({"unchecked"})
Class<X> result = (Class<X>) loader.loadClass(className);
loader.close();
return result;
}
}
If you want to reload a changed classfile, you don't have to ask the classloader which has already loaded the pre-version, this will deliver always this already loaded version . Use a new classloader, for example
...
Class<?> reloadClass(String classLocation, String className) throws Exception {
URL url = new File(classLocation).toURI().toURL();
URLClassLoader cl = new URLClassLoader(new URL[] { url }, String.class.getClassLoader());
Class<?> c = cl.loadClass(className);
cl.close();
return c;
}
...
EDIT:
Ok, i tested it with a simplified version of your code. The changes in my is only a little bit cosmetic (copied from Binkan Salaryman). It works.
public class Autorunner extends Thread {
private Class runnable;
private File output;
public Autorunner(Class runnable, File output) {
this.runnable = runnable;
this.output = output;
}
#Override
public void run() {
try {
//This is only to get the location of the classfile
URL url = Test.class.getProtectionDomain().getCodeSource().getLocation();
Class runtimeClass = reloadClass(url,Test.class.getName());
Method method = runtimeClass.getMethod("main", String[].class);
method.invoke(null, (Object) null);
System.out.flush();
} catch (NoSuchMethodException | SecurityException | IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException ex) {
}
}
Class<?> reloadClass(URL classLocation, String className) throws ClassNotFoundException, IOException {
URLClassLoader cl = new URLClassLoader(new URL[] { classLocation }, String.class.getClassLoader());
Class<?> c = cl.loadClass(className);
cl.close();
return c;
}
Here is a tested, fully working, demonstrative class hotswap test program.
Before running, you need to create "./Test.jar" and "./tmp/Test.jar" and put in a file "Test.class" (without package in code, without folder in jar) you've compiled with a main method and a System.out.println statement.
If something does not work as expected, be sure to give detailed error descriptions and what you've tried.
Code for "./Autosaver.jar" (name doesn't matter):
public class Program {
private static final File Test_classLocation = new File("./Test.jar");
private static final File alternativeTest_classLocation = new File("./tmp/Test.jar");
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
System.out.println("Test.class location = " + Test_classLocation.getAbsolutePath());
System.out.println("alternative Test.class location = " + alternativeTest_classLocation.getAbsolutePath());
while (true) {
testInvocation();
swapFiles(Test_classLocation, alternativeTest_classLocation);
Thread.sleep(3000L);
testInvocation();
swapFiles(Test_classLocation, alternativeTest_classLocation);
Thread.sleep(3000L);
}
}
private static void testInvocation() throws IOException, ClassNotFoundException {
Class<?> Test_class = reloadClass(Test_classLocation.toURI().toURL(), "Test");
invokeMain(Test_class, new String[0]);
}
private static void swapFiles(File a, File b) throws IOException {
Path aTempPath = new File(b.getAbsolutePath() + ".tmp").toPath();
Files.move(a.toPath(), aTempPath);
Files.move(b.toPath(), a.toPath());
Files.move(aTempPath, b.toPath());
}
private static <X> Class<X> reloadClass(URL classLocation, String className) throws ClassNotFoundException, IOException {
URLClassLoader loader = new URLClassLoader(new URL[]{classLocation}, null);
#SuppressWarnings({"unchecked"})
Class<X> result = (Class<X>) loader.loadClass(className);
loader.close();
return result;
}
private static void invokeMain(Class<?> mainClass, String[] args) {
try {
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke(null, new Object[]{args});
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new Error(e);
} catch (InvocationTargetException e) {
System.err.println("invocation of " + mainClass.getName() + ".main(" + String.join(",", args) + ") threw an exception:");
e.printStackTrace();
}
}
}
Code for "./Test.jar!Test.class":
public class Test {
public static void main(String[] args) {
System.out.println("old " + Test.class);
}
}
Code for "./tmp/Test.jar!Test.class":
public class Test {
public static void main(String[] args) {
System.out.println("new" + Test.class);
}
}
Output:
Test.class location = D:\rd\test\out\artifacts\Autosaver\.\Test.jar
alternative Test.class location = D:\rd\test\out\artifacts\Autosaver\.\tmp\Test.jar
new class Test
old class Test
new class Test
old class Test
new class Test
old class Test
new class Test
old class Test
new class Test
old class Test
new class Test
old class Test
new class Test
old class Test
new class Test
old class Test
new class Test
...
You can download a zip of the demo here.
I am receiving a compile time error with the following code. The first code block scans in a text file and provides a get method for retrieving the largest value in the Array List. That block of code compiles fine. The second block of code is where I'm having difficulty. I'm fairly new to programming and am having difficulty understanding where I've made my error.
public class DataAnalyzer {
public DataAnalyzer(File data) throws FileNotFoundException
{
{
List<Integer> rawFileData = new ArrayList<>();
FileReader file = new FileReader("info.txt");
try (Scanner in = new Scanner(file)) {
while(in.hasNext())
{
rawFileData.add(in.nextInt());
}
}
}
}
public int getLargest(List<Integer> rawFileData){
return Collections.max(rawFileData);
}
}
This is the Tester Class I am attempting to implement. I am receiving a compile time error.
public class DataAnalyzerTester {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Enter your fileName: ");
}
public void printLargest(DataAnalyzer rawFileData)
{
rawFileData.getLargest();
System.out.println(rawFileData.getLargest());
}
}
I tryed to run your code, you have problem in the line 14 of the DataAnalyzerTester, you need to pass a parameter of List<Integer> to the method getLargest().
Try your Tester this way:
public class DataAnalyzerTester {
/**
* #param args
* the command line arguments
*/
public static void main(String[] args) {
System.out.println("Enter your fileName: ");
}
public void printLargest(DataAnalyzer rawFileData) {
List<Integer> example = new ArrayList<Integer>();
example.add(0);
example.add(1);
example.add(2);
int result = rawFileData.getLargest(example);
System.out.println(result);
}
}
-------------- EDIT --------------------
Try something like this:
public class DataAnalyzer {
private List<Integer> rawFileData;
public DataAnalyzer(String fileName) throws FileNotFoundException {
rawFileData = new ArrayList<>();
FileReader file = new FileReader(fileName);
try (Scanner in = new Scanner(file)) {
while (in.hasNext()) {
rawFileData.add(in.nextInt());
}
}
}
public int getLargest() {
return Collections.max(rawFileData);
}
}
public class DataAnalyzerTester {
public static void main(String[] args) throws FileNotFoundException {
DataAnalyzer analizer = new DataAnalyzer("info.txt");
System.out.println(analizer.getLargest());
}
}