In Java, it is possible to get the class and method that called the current method (the method in which you get the StackTrace).
Can I get the arguments that were passed to the method that called this method?
I need this for debugging purposes.
Eg:
baseClass {
initialFunc(input) {
var modifiedInput = input + " I modified you";
otherClass.doSomething(modifiedInput);
}
}
otherClass {
doSomething(input) {
//GET THE ARGUMENTS PASSED TO THE METHOD OF THE CLASS THAT CALLED THIS METHOD
}
}
Can one get this information from the stacktrace, or are there other means?
(Note that I need to be able to do this in runtime and cannot actually change the source of baseClass, this is going to be a feature of my debugging class that does not know the source beforehand)
I don't believe this is possible using the standard Java API.
What you could do is to use AspectJ, place a point-cut at the calling method, save the arguments, place a point-cut at the called method and pass on the arguments.
Another option (slightly more advanced) is to use a custom, bytecode-rewriting, class loader that saves the original arguments, and passes them on as extra arguments to the next method. This would probably take a day or two to implement. Suitable frameworks are BCEL or ASM.
I think this could be possible, because input is out of scope but isn't yet accessible for garbage collection, so the value still exists, but unfortunately I don't believe there is an default API way to access it. This could be maybe possible with a custom implemented NDC (nested diagnostic context) for the logging approach.
I'm not sure why you'd ever want to do this in Java?
The only way I can think of is to create a custom wrapper object for the passed string, thus sending the reference to the wrapper instead of a new string each time.
I'd advice against it, though, since it clutters your original code, and makes it even more error prone.
Might this problem not be solved using a debugger, like the one built into eclipse, to inspect your state?
In my case, I needed to get a parameter value has been passed to a method in a certain stack frame to be used later within the execution flow
I used ThreadLocal to store it and when I needed it I was able to retrieve it at any point in code as I declared it as public static
here is a skeleton example
public static final ThreadLocal<SomeType> IMPORTANT_THREAD_LOCAL_FOR_BLA = ThreadLocal.withInitial(whatever);
methodWithImportantParam(SomeType importantValue){
// save it in the static threadLocal Field
this.IMPORTANT_THREAD_LOCAL_FOR_BLA.get()=importantValue;// code to set the value
// continue method logic
}
and somewhere in code where you need that value
YourClass.IMPORTANT_THREAD_LOCAL_FOR_BLA.get()
but make sure the execution flow that you set the value then you retrieve it
hope my answer add something valuable to this question
You can get name of caller method and its class, but you have to add some code in current method:
public static void main(String[] args) throws Exception {
call();
}
private static void call() {
Exception exception = new Exception();
for(StackTraceElement trace : exception.getStackTrace()){
System.out.println(trace.getMethodName());
}
}
This will print "call" and "main", methods name in called order (reverse).
This is possible using Reflection API !
public class StackTrace {
public static void main(String args[]) {
StackTrace st = new StackTrace();
st.func();
}
public void func() {
OtherClass os =new OtherClass();
os.getStackTrace(this);
}
}
class OtherClass {
void getStackTrace(Object obj) {
System.out.println(obj.getClass());
}
}
Related
Is there a way to detect usages of java method reference (double colon) operator inside the code?
I need to discover all instance/static method references used in a given class in order to be able to detect some errors (must verify that the target method has a particular annotation - #Good in the below example) during build time. As by convention a method reference should be used only to some of the methods when it is passed to a constructor of some helper class (Info in the below example).
class X {
Info init() {
return new Info(X::beta); // good code: target method has #Good annotation
return new Info(X::alpha); // bad code: target method has no #Good annotation
}
void alpha() {
}
#Good
void beta() {
}
}
The intention is to be able to click on the method reference as this makes it easy to follow as otherwise if just passing Method instance or just method name it would lack this ability.
(The example is not very good but I'm now allowed to share more details, sorry about that!)
I can see IntelliJ IDEA "knows" about them - when you ctrl+click on them it navigates to the target method so there should be some form of a static analysis used there.
I'm already using ObjectWeb ASM to detect invocations to certain methods but it seems it lacks the ability to detect method references (::)
EDIT:
Just a note that you can also pass new Info(x -> x.alpha()) as #Thomas below mentioned in the comments but this would not pass our review process but I guess the additional ability to detect it would not hurt.
EDIT2: What exactly are you trying to achieve with these checks? What makes beta worthy of receiving the annotation?
Answer:
When the init() method is called we obtain the Info instance and from it obtain the lambda which must be a method reference. Then we use javassist ProxyFactory and create a sub-class of class X then instantiate it and intercept all its methods via setting a method handler. So now it is safe to execute the lambda without allowing it to make any side effects - the method body is skipped and the only thing we do is to capture which is the X method that the lambda actually is calling - in the example this will lead to a java.lang.Method instance pointing to X.beta or X.alpha method. Then we can check if it has the #Good annotation and proceed accordingly - which is to call the lambda without any proxying, but that call might happen later, like a millisecond later or an hour later. If there is no #Good annotation we cannot proceed - it is a bug.
So the problem is that this will happen at runtime later and there might be a bug not caught early enough and that is the reason I would like to inspect the X class at build time and catch all the bugs :)
This is a bit of a shot in the dark, as I'm neither very proficient with ASM nor sure if this approach addresses your problem. Having said that, I found that, in a similar setting, asm.MethodVisitor calls MethodVisitor.visitInvokeDynamicInsn(...) for (some? all?) method references.
E.g., if I compile this variant of your class X along with an Info:
class Info {
public Info(Runnable alpha) {}
}
class X {
Info init() { return new Info(this::alpha); }
void alpha() {}
}
... and I then feed the resulting X.class into a mini ClassVisitor + printing MethodVisitor (Groovy for brevity):
class MyMethodVisitor extends MethodVisitor {
MyMethodVisitor(MethodVisitor parent) { super(Opcodes.ASM8, parent) }
#Override
void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
println "visitInvokeDynamicInsn($name, $descriptor, $bootstrapMethodHandle, $bootstrapMethodArguments)"
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
}
}
class MyClassVisitor extends ClassVisitor {
MyClassVisitor() { super(Opcodes.ASM8) }
#Override
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
println "Starting method '$name'"
new MyMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions))
}
}
def clr = new ClassReader(new File("./X.class").bytes)
clr.accept(new MyClassVisitor(), ClassReader.SKIP_FRAMES)
Then the method visitor prints, amongst other details, a call to visitInvokeDynamicInsn from within the method visitation of X::init with the desired X::alpha among the arguments (the xyz being my local package):
Visiting method '<init>'
Visiting method 'init'
visitInvokeDynamicInsn(run, (xyz/X;)Ljava/lang/Runnable;,
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; (6),
[()V, xyz/X.alpha()V (5), ()V])
Visiting method 'alpha'
So it would seem possible to peel the method out of those arguments. I am not sure if this reliable (e.g., whether this bytecode is guaranteed by specification, or whether it can depend on compilation/optimization details).
I'm very new to programming language. My question might not even make sense. My environment is using java and trying to implement both ios and android apps in the same automation testing framework.
So, the idea is that any test script should be able to run on both the apps. Ex: one signin test script should be run for both ios and android.
I've decided to use interface and class implementation approach. The problem I'm facing is with test data. My company doesn't want to use excel. They want to use json for test data.
Here's my problem, look at the following line of code:
ValidBuy goodBuy = JsonFileReader.loadDaTa(TestBase.DATA_PATH, "good-buy.json", ValidBuy.class);
As you can see I have a class "ValidBuy" that has all the getters for a particular json file. I have another class "JsonFileReader" which takes the json filePath, fileName, and a class as an input and returns the data for that class name that I passed in. For this example I've passed ValidBuy.class
So, when I run a positive test, I'm passing "goodBuy" variable which is of type "ValidBuy". The problem starts here.
The test case is now specified with the data from goodBuy because it's type is "ValidBuy" and I'm passing goodBuy as a parameter.
Look at one of my extracted methods:
private void enterBuyInfo(ValidBuy goodBuy) {
itemPage = nativeApp.getItemPage(goodBuy);
itemPage.setItemName(goodBuy.getItemName());
itemPage.setItemSize(goodBuy.getItemSize());
itemPage.setItemDigitSSN(goodBuy.getSsn());
itemPage.clickContinue();
}
You can see those getters I'm using are coming from ValidBuy class.
If I run this test with the data for a badBuy:
InvalidBuy badBuy = JsonFileReader.loadDaTa(TestBase.DATA_PATH, "bad-buy.json", InvalidBuy.class);
It fails because now I have to change "ValidBuy" class with "InvalidBuy" class. Since, changing the parameter in the extracted method in every run is not possible, how can I make it more generic?
I want something like this:
TestData data = JsonFileReader.loadDaTa(RESOURCES_PATH, "good-client.json", InvalidBuy.class);
Here, TestData is generic. It could either be a class or interface (I don't know if that's possible) and the return type will be specified by whichever class I pass into the loadData() method. In this case InvalidBuy.class
The extracted method should look like this:
private void enterBuyInfo(TestData data) {
itemPage = nativeApp.getItemPage(data);
itemPage.setItemName(data.getItemName());
itemPage.setItemSize(data.getItemSize());
itemPage.setItemDigitSSN(data.getSsn());
itemPage.clickContinue();
}
If I can do this, I can use those extracted methods to create more tests.
I know I wrote a lot. I've only tried to make it as clear as possible. If it doesn't make any sense, just disregard it.
Any suggestions, ideas, code samples will be highly appreciated.
Firstly let me see if I understand your question. I think you are saying that loadData may return a value of type ValidBuy or InvalidBuy and you want to pass into it the class that you want returned. You then want to know how to use an interface that might represent either of these classes in your test methods so you can test various return values (both valid and invalid). You use the term "generic" in your question but I'm guessing you don't mean to use it in the specific way it's used in Java.
If I've understood your question correctly, then here's an answer:
Passing the class you wish to have returned into a method is an unusual usage and almost certainly not ideal. Better OOD would be to extract the common methods for all objects returned from loadData into an interface.
So:
interface Buy {
String getItemName();
boolean isValid();
}
class ValidBuy implements Buy {
#Override
public boolean isValid() {
return true;
}
...
}
class InvalidBuy implements Buy {
#Override
public boolean isValid() {
return false;
}
...
}
class JsonFileReader {
Buy loadData(Path path) {
...
}
}
Then your tests can look like:
#Test
void testValidBuy() {
assertTrue(reader.loadData(validPath).isvalid());
}
#Test
void testInvalidBuy() {
assertFalse(reader.loadData(invalidPath).isValid());
}
I realise I've simplified it a bit but hopefully you get the idea.
I'm writing an application that has an instance of a class that contains the esper engine. There are a number of instance variables that I would like to read and set using instance method calls from the EPL in the engine. I don't get any compilation errors and the code runs. But the instance method is not called.
epl statements:
module myModule;
create variable com.tp.main.MyClass myClass;
select myProperty from MyEvent unidirectional, method:myClass.getMyProperty() as myProperty;
A hint could be that if I don't use the method: key word in front of the method call I get an error that myClass.getMyProperty class could not be found. The documentation sometimes uses the method: key word and sometimes not in the examples for calling instance methods from Class-type variables.
I have also tried using the addVariable method in the API with the same results.
code for the method.
public Result getMyProperty() {
Result result = new Result();
result.setResult("propertyValue");
logger.info("This method was called");
return result;
}
The class Result is a POJO with getter and setter for a string.
public class Result {
private String result;
public String getResult() {
return result;
}
public void setResult(String str) {
result = str;
}
}
What am I missing?
You could look at a regression test class. The specific one you may want to look at is ExecFromClauseMethodVariable. Maybe your code does not assign a value to the variable?
Github:
https://github.com/espertechinc/esper/blob/3e396d77308532b202ee452100eaaf9e7a044906/esper-regression/src/test/java/com/espertech/esper/regression/epl/fromclausemethod/ExecFromClauseMethodVariable.java
Problem solved and I thought it might be useful to share the solution. Credit to user650839 who pointed me in the right direction. Here is what ended up fixing the problem.
I reverted back to declaring the variable in the runtime configuration API. I found that I must register the variable class, initialize it with the instance object (this) and finally import the class. Here is the snippet of code that does this configuration in the runtime configuration API.
Configuration configuration = new Configuration();
configuration.addVariable("myClass", com.tp.main.MyClass.class, this);
configuration.addImport(com.tp.main.MyClass.class);
epService = EPServiceProviderManager.getProvider(trade.getTradeName(), configuration);
It seems there is a limitation when declaring the Class variable in the EPL. You cannot initialize it with the instance object you want to use. In the runtime configuration API I was able to initialize it with "this" instance of the object which contains all of the instance variables I want to access from the EPL.
The EPL statement did not change. However, it does seem that you must use the key word method: in front of the method call or you get an error "cannot find class..."
Can you help me understand why we call the parent class here? I found a download class that seemed simple enough but could use help wrapping my brain around the first method.
public class DownloadHandler {
public static void main(String[] args) throws Exception {
DownloadHandler d = new DownloadHandler();
d.URLSetUp(args[0]);
}
....
}
I am trying to instantiate the handler in a for loop and getting an error.
DownloadHandler file = new DownloadHandler("http://example.com/"+cleanLink+"/"+filename+".pdf")
It says "DownloadHandler() in DownloadHandler cannot be applied to (java.lang.String)"
Your DownloadHandler class has a static void main method, which is the single point of entry when executing command-line programs.
That method is not a constructor.
What it does is initialize a new instance of DownloadHandler and invoking an instance method on that object by passing the given String argument.
Not sure what's the usage there.
In order for your initialization to compile, you probably want to add a constructor that performs similar operations, given a single String parameter in your case.
For instance:
public DownloadHandler(String s) {
URLSetUp(s);
}
Java adds a default constructor to every class that doesn't provide one. A constructor is a method without a return type. So, in your case the default constructor DownloadHandler() is automatically added to your class and it does not take any parameters while you are trying to initialize it with a String.
The String you are using in main method right now is coming from console from user.
From your code its obvious that you want to pass a argument via command line parameter. But when you are initiating DownloadHandler, you are passing that string here which is not you should be doing.
There are two things you can do now.
Pass the string via command line parameter
java DownloadHandler yourstring
Write a constructor which accepts the string. In your code outside of your main method
String url;
public DownloadHandler(String str)
{
url = str;
}
Now call
d.URLSetup(url);
Hope this will clear your doubts.
I am fairly new to Java, I learned it in college but haven't really touched it much since. I am trying to learn it again and I am making a simple client server game but have run into some issues.
Basically what I am trying to do is make it so that I have a class with all the commands a player can do and I want to call that command based on what they send the server.
Client sends message such as "kill monster".
Server should split the string and call the method kill('monster');
I could obviously map out the commands with a switch but I wanted to try out reflection and dynamically calling the method based on the string sent. This would make my code a bit cleaner I think since I would only need to add new methods and a handler for when there is no such method.
In PHP this is really easy you can just call the function by saying myclass->$mymethod('a string');
I already have the client and server set up to have the client type a string and send it to the server which saves it in the variable message.
What I have tried in Java is this:
String[] parts = message.split(" ", 2);
Command.class.getMethod(parts[0].toLowerCase(), String.class).invoke(parts[1].toLowerCase());
But this does not work for me and I get an error
java.lang.IllegalArgumentException: object is not an instance of declaring class
Command.java is simple and is this:
public class Command {
public Command(){
}
public void kill(String monster){
System.out.println(monster + " Kills you!");
}
}
What am I doing wrong. The one thing I am particularly not sure about is the second parameter of getMethod().
The error itself is because the method that you're trying to invoke is an instance method, and you need an instance of that command to invoke it (right now, you're trying to invoke the kill method on an instance of the String class -- the first argument you're passing). See this answer or the Method.invoke() javadoc for details.
But if you need reflection in your application code, it usually means you're not on the right track (at least from an OOP point of view).
In your particular case, the various commands should not be modeled as methods of the same class, but as instances of it (see command pattern, for instance, or even strategy pattern, as useful examples).
Since you only want to have one instance per command (and you need to find the proper command by name), an enum would probably be the most suitable choice here:
enum Command {
KILL {
#Override public void execute(String arg) {
System.out.printf("%s is dying...", arg)
}
},
KISS {
#Override public void execute(String arg) {
System.out.printf("I'm kissing %s!", arg)
}
};
public abstract void execute(String arg);
}
Then, your code becomes simply:
Command.valueOf(parts[0].toUpperCase()).execute(parts[1].toLowerCase());