Need to access method/data from calling java methods - java

My challenge. I have a class that is being accessed by third-party code, and I want to audit what's calling it. To do this I need to access the object or at least the data of the calling objects. Class name from the stack trace isn't far enough.
The real example:
I've extended the BaseStandardCredentials class from Jenkins. I want to audit the public Secret getPassword() method. But to audit, I need the job details. Anyone using the more normal methods is audited elsewhere. But there are a few plugins/shared pipeline code which pull all credentials from the system, effectively hiding the job involved, and just cherry-pick the credential they need (http_request is the main culprit but there are others)
My basic thought is to audit the getPassword() function as that's exactly who/what is getting the data. I can see in the stack trace the calling methods but that's no good if I can't get the job name/number from one of the execution objects.
Any way to do this? Or any way to at least tag the object in a way it shows up in a stack trace?

Related

Know what class instantiated a class

Basically, I'm creating an API. I have multiple endpoints, implemented as controllers. If an endpoint is called, it instantiates a parserclass, adds some options (like permitted parameters etc).
But now: If the parser fails, I want it to present a nice error, containing a link to the docs of the endpoint that actually failed.
I use b4j, which works on top of Java, so any relevant Java code should be easy to convert. In b4j, any endpoint is resembled by a class.
E.g.
I have a 'getPOIByCity' endpoint.
If a user calls this method, a city parameter is required. If he/she omits the parameter, I want the user to be greeted with an error containing:
1 required parameter missing: city
See the documentation: http://www.link.to/doc/getPOIByCity
My first thought was to do some kind of stack tracing to know which class instantiated the parses class, but I cannot believe that is the way to do this.
C# has the slightly more elegant 'Caller information' attributes.
Any insights in how to do this?
As another-dave already mentioned, it would make a lot more sense to delegate the exception to the calling class (the controller) and show the error from there.
But just to give an answer to your problem, you can use
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
String parentClass = stackTraceElements[2].getClassName();
Class cl = Class.forName(parentClass);
to achieve what you want.

how to create a custom stacktrace in java with additional info (like parameters)?

I was assigned a task to create a custom stacktrace like output to a log file for some specified functions, but instead of just using the class and method names I would also have to output the parameters and their values.
This is supposed to be a separate jar that could run on any java project, after.
I don't even know if such thing is possible, let alone where to start.
Any help would be appreciated.
EDIT: there is other library that does that by using native VM api: https://github.com/cretz/stackparam it also modifies Throwable class to always print that modified stacktrace.
The only possible way I can think of is using agents and instrumentalization, but agent needs to be added to startup command line.
Then I would register transformer to transform every class (remember that some basic java classes might be already loaded) using ASM library and add code to beginning of every method invocation to manually track each method class and pass it to my library that would track them:
// note that parameters names might not exist in runtime if code was compiled without a flag to include them.
public void doSomething(String name, int something) {
MyLib.enterMethod(ThisClass.class, new MethodSignature(void.class, String.class, int.class), new Argument("name", name), new Argument("something", something));
try {
// original code
} finally { // so we don't need to care about return in the middle of original code or exceptions
MyLib.exitMethod();
}
}
enterMethod would add invocation frame to some queue and exitMethod would remove last added frame. Note that you should have separate queue for each thread, use some Map<Thread, MyFrame> or ThreadLocal it might be good idea to use some weak references for threads.
And then you could use frames from that queue to create own stacktrace.
But doing something like that might decrease performance a lot - not even just because cost of this code, but adding that to every setter/getter might cause that methods to never be inlined and affect performance even more.
So it is possible but I really don't recommend doing something like that.
Also some other transformers added by other libraries might affect results, it might be good idea to also compare your stacktrace with original stacktrace to find any missing methods that you didn't transform - like native ones, and add them to your stacktrace but without that additional data.
If you really need to support native methods too - then you can create more advanced transformer that would add enterMethod/exitMethod before and after call to native method.
Also if this is only for debugging you could use debugging API so it would only work as a debugger.

Know when value of any variable defined inside the class is changed

I have defined a class which acts like a model/pojo. The class has many keys/variable. I have implemented custom solution for storing the POJO on disk for future uses. Now what I want to do is that whenever any value in the class/POJO is changed, I should call a method which sync the fresh changes with file on disk.
I know I can define setter for each variable. But it's quite tedious to do for 100s of direct and sub fields, and even if I define setter for each field, I have to call sync function from all the setters.
What I need is single proxy setter or interceptor for all change pushes to variables in class.
I am using this in an android application, so whenever the user enters new details in his/her account I have to store those details at that specific instance of time for preventing the data loss. I am using GSON for serialising and de-serialising.
Sorry for using vague terminologies, never been to college :|.
The easiest solution is indeed to use a setter. You only have to create one for each field you want to monitor, and most IDEs generate them for you or you can use something like Koloboke, so it being tedious isn't really an argument.
A proxy class or reflection would also be possible, but that is pretty hacky. Another way would be an asynchronous watcher/worker that checks for changes in you POJO instances, but even that seems unnecessarily complicated.
Apart from that you might need to rethink your POJOs structure if it has that many fields.
The problem with persisting(in your case writting to a disk) entity on each property update is that most of the updates are modifying more then one property. So in case you have a code like this:
entity.setA(avalue);
entity.setb(bvalue);
entity.setc(cvalue);
You would write it to the disk 3 times, which is probably not a best way, as it takes more resources, and 2 out of 3 writes are unnecessary.
There are several ways to deal with it. Imagine you have some service for saving this data to a disk, lets name it entityRepository. So one option is manually call this entityRepository each time you want to save/update your entity. It seems to be very uncomfortable, comparing to calling this automatically on setter call, however, this approach clearly shows you when and why your entity is persisted/updated, in your approach it's unclear, and can lead to some problems future problems and mistakes, for example, in future you will decide that you now need to update one of the properties without immideately persisting, then it appears that you will need 2 setter, one with update, and one without...
Another way is to add version property, and when its setter is called inside this setter call entityRepository.save(this).
The other way is to look at AOP, however anyway I don't recommend persist entity on any change, without having control over it.
You are talking about data binding. There is no built-in way for that so you have indeed to sync it yourself. Look into How to Write a Property Change Listener. There are also lots of other approaches to this, but as said no built-in way.

Provide data to a method using aspect oriented programming

I'm learning AOP and am comfortable with Pointcuts, Advices etc.
What am going to ask, am pretty sure is not possible, but want to ask anyways.
I have a method which takes a userId, fetches the user's record from a database and then does something to the record. I have like twenty different methods that do different things, but all of them take the userId as input and fetch the record from database. This to me looks like a cross cutting concern that can be pulled into an aspect.
But how? I know I can access the arguments (userId in this case), access the return value of the method and catch the methods exception. But how do I give the method something to work with (record in the database in this case?)
public String printUserDetails(String userId)
{
Record record = Database.fetchRecord(userId);
System.out.println(record.getDetails());
return record.getTitle();
}
So, is there a way to pull that database accessing code into an aspect?
One way I can think of is declare something like the following for input
class RequestObject
{
String userId;
Record record;
}
and inject the record in the Aspect and then call proceed(). But this somehow feels wrong.
IMO, resolving a user, using the userid, is not a cross-cutting concern and hence aspect is not the right way. The first landing page that receives a userId should actually resolve it to UserRecord and from then on, the userRecord should be the one moving around in the application.
A simple analogy I can draw to your scenario from one of my applications is, all authenticated servlets expect the servletRequest.getRemoteUser() to return the valid user login corresponding to the user sending the request. We decorated the HttpServletRequest to resolve this to a User object in our application and all the authenticated servlets downcast the HttpServletRequest to AuthenticatedServletRequest and extract this object. No one else within the application tries to resolve a user login anymore.
You cannot access a method's local variables from AspectJ if this is what you wanted to know.
The rest of the question is rather about design and the answer dependent on what you want to achieve. You can avoid code duplication in multiple methods using a template method design pattern. You can inject real or mock objects into classes if you refactor them to have a member instead of local variables. It is another question if you create the member by directly refactoring your classes or via AspectJ's (ITD)[http://www.eclipse.org/aspectj/doc/next/progguide/starting-aspectj.html#inter-type-declarations] mechanism. A third question would be if you possibly want to use an aspect for caching in order to avoid fetching the same object from the database multiple times.
I am not sure what exactly you want to achieve, so I cannot answer more specifically.

Modifying Java classes at runtime to make instance fields transient -- will it work?

We have an annoying log message coming from Weblogic whenever we invalidate the HTTPSession when it has objects that are not serializable (which we don't care about but which is polluting our logs). Tweaking our log4j configuration to not log this message does not appear to be an option (the message is coming from a generic Weblogic class and we dont want to supress all messages from this class and other than writing our own adapter to look at messages about to be written to the log and suppressing the deserialization error messages I dont know how we would do it through log4j configuration).
In order to temporarily fix this to clean up our logs I want to intercept calls to add objects to the HTTP Session so that the class of the object being added (and any objects in its object graph) have their instance variable declarations changed to transient. Figured I would intercept calls by adding an HTTPSesssionEventListener and then modifying the Class's instances variables using the reflection libraries.
Does anyone know if this would work?
Does anyone have a better suggestion and/or know if this will/will not work like I would want it to?
Yes. Build to spec!
In compliance with the Java Servlet specification:
The distributed servlet container must throw an IllegalArgumentException
for objects where the container cannot support the mechanism necessary for
migration of the session storing them.
So if you want to avoid this message (and write good portable and distributable code), just make the object you put in HttpSession implement the Serializable interface.
Seriously, I can't believe you are thinking to a solution to workaround the real problem instead of fixing it.
One option if would be
tail -f yourlog | grep -v "annoying line here" > cleanLogFile
Which is much less intrusive. But creates a second file.
I did this for a project where I needed to trace an specific problem and all those stupid messages where getting in the middle.
Why go to that trouble? It sounds just as easy to mark them all as Serializable, as long as all the objects in question are yours.
If that's not possible, .aAnother way is to have a class registered within your webApp that implements HttpSessionAttributeListener. In the attributeAdded method print the sessionID and then serialize the session data:
public void attributeAdded(HttpSessionBindingEvent hsbe)
{
// Handle the details yourself here.
ObjectOutputStream.writeObject(hsbe.getValue())
}
Try modifying the ObjectOutputStream, not the serialized object.
If you extend ObjectOutputStream, you can override the replaceObject method and clear the non-serializable fields or re-create the object without the non-serializable properties.
Another option is to use reflection to nullify the non-serializable fields of the session objects, but that is very risky as a session object could be loaded back and appear to work but then throw a NullPointerException hours or even months later (when the field you nullified is eventually referenced.)
Modifying the classes will probably not work.
It is possible to extend ClassLoader and tweak flags on instance fields but:
If it's your class that contains the non-serializable field then it's simpler to change the code yourself to make it transient
If it's not your class then it will be loaded by a different ClassLoader from your application classes so by the time you see the object it will be too late (because the class has already been loaded.)
You cannot set the transient flag on an individual object, only on a class.

Categories

Resources