Tracking where variables are used when calling another class - java

This is a bit out of the box but I have a requirement to track input to methods and track where they are used in another classes. Is there any good way to do this given the code could be refactored and changed and I want to ensure the mapping maintains it accuracy?
// doSomething is under my control
#Endpoint("testEndpoint")
public void doSomething(String name, int age, String surname) {
String fullName = name + " " + surname;
// SendRequest is generated code (not under my control)
SendRequest sr = new SendRequest();
sr.setFullName(fullName);
sr.setUserAge(age)
this.sender.send(sr);
}
So they ouput might be
testEndpoint
INPUT <> OUTPUT
---------------
name -> fullName
age -> userAge
surname -> fullName
I need this information at runtime and before the method is executed. My current solution is inspired from Dozer and uses reflection and a custom annotated transformation class which I can read at runtime to gather the dependencies and pull out this information - in effect turning a type safe language into a type unsafe language.
Just wondering if there is a better solution or some library out there that can do this.

How about using AspectJ?
You could define a point cut that matches the method(s) of which you'd like to capture the arguments and then define an advice that actually captures and stores, tracks, whatever the argument values.
public aspect ExampleAspect {
pointcut doSomething() : call(public void doSomething(String,int,String));
before(): doSomething(){
System.out.println("INPUT <> OUTPUT");
System.out.println("---------------");
System.out.println("name -> "+ this.JoinPoint.getArgs[0]);
System.out.println("age -> "+ this.JoinPoint.getArgs[1]);
System.out.println("surname -> "+ this.JoinPoint.getArgs[2]);
}
}

Related

How to reuse a method, but adding more features to the second one?

I have a method addUser() in Class Register that works as a regular form, asking the user of the system things like "firstName", "lastName", "username", and "password".
class Register {
void addUser() {
try {
System.out.print("First Name:\n> ");
String firstName = userInput.nextLine();
(...)
}
}
Only once the user is registered and logged in, they can add new customers using a method in the Class Customer, that performs just as addUser(), but adding more fields, like "address" and "email".
class Customer {
/*At the moment I'm redoing all over again just because it works.
and because I didn't know how to implement in any other way.*/
void addCustomer() {
try {
System.out.print("First Name:\n> ");
String firstName = userInput.nextLine();
System.out.print("Address:\n> ");
String address = userInput.nextLine();
(...)
How can I reuse the addUser() method in the Customer class using the fact that the user is logged in as a condition? Thanks in advance for your time.
Firstly I'd suggest you move the addUser method out of both Register and Customer - that way you have some guarantee that the method is not tied to any particular use case and thus will be reusable.
Then you're going to need to create mechanism for both calling contexts (Customer and Register) to interface with the addUser method. For this you could use some kind of data object which they exchange, and then once they get it back they pull out the data they want, or you can let both them implement an interface that the addUser method can be passed with which it will and back what it captures.
I don't feel using inheritance is a good enabler of reuse in this case. Inheritance is something which must be done carefully as it can also increase dependencies. It is important to adhere to the idea that when using inheritance the 2 types being inheritance should actually have some inheritance relationship - I don't think this is the case here.
So then the Register user can call this addUser method and the Customer class can call this addUser method and then add the extra fields it includes.
You can use a static boolean value set to false (e.g: isLoggedIn). If the user logs in successfully, change it to true. Then call this method in the Customer class (create Register object) only if the isLoggedIn variable equals true
I'll rephrase your question as "How can I make my code less repetitive and easier to maintain through reuse?"
The solution in that case is to create a reusable function that takes care of the repeated task of printing a prompt and reading in from the keyboard:
public class Console {
public static String prompt(String field) {
System.out.print(field + ":\n> ");
return userInput.nextLine();
}
}
Then you can simplify your other functions:
void addUser(){
try {
String firstName = Console.prompt("First Name");
(...)
}
}
void addCustomer(){
try {
String firstName = Console.prompt("First Name");
String address = Console.prompt("Address");
(...)
}
}
Other forms of reuse are object composition and inheritance, but I feel that they are not necessary based on the example code that you gave.

Can the visitor modify the object it visits

When using the Visitor Pattern, can the visit(object) method update or modify the object it visits or is it simply supposed to only use the object in performing some computation and returning the computation result?
Thanks
All examples of the visitor pattern I've seen don't change the objects visited.
But as far as I understand the definition given on Wikipedia, it's just not defined what the visitor can do with the visited objects.
Even the definition from Gang of Four (also in the wiki article) doesn't touch that subject:
Represent an operation to be performed on elements of an object
structure. Visitor lets you define a new operation without changing
the classes of the elements on which it operates.
Therefore I'd say that a visitor may call any accessible method on an object it visits as this is not part of the visitor pattern - the visitor pattern just describes how this objects are made accessible.
In the following example the mailer is the "element" and the "visitor" is the lambda expression which is passed to send().
As you can see here (and in many other examples) the visitor can modify the element's internal state. That said, changing the element's internal state is not a requirement: it could be that the visitor will call some of the element's methods in order to do all sorts of actions: pass a message to another object, run a computation and print to screen the result and etc.
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
Mailer.send(mailer ->
mailer.from("me#hotmail.com")
.to("you#hotmail.com")
.body("what's up bud?")
);
}
}
class Mailer {
String fromStr;
String toStr;
String bodyStr;
public Mailer from(String from) {
this.fromStr = from;
return this;
}
public Mailer to(String to) {
this.toStr = to;
return this;
}
public Mailer body(String body) {
this.bodyStr = body;
return this;
}
public static void send(Consumer<Mailer> loader) {
Mailer mailer = new Mailer();
loader.accept(mailer);
System.out.println(mailer);
// ... send the email
}
#Override
public String toString() {
return "From: " + fromStr + "\n" +
"To: " + toStr + "\n" +
"Body: " + bodyStr;
}
}
The visitor pattern is not meant to modify the object's state. Instead, its purpose is to implement specific functionality.
A paradigm would be adding the functionality of counting the elements of a tree, an example I explain in this Case Study: Applied Design Patterns and Software Architecture.
The CharacterVisitor which holds the count value of the total visited CustomCharacter class has the following structure:
/**
* Implements the Visitor pattern
*/
class CharacterVisitor
{
private int count = 0;
void visitCharacter(CustomCharacter customCharacter)
{
count++;
}
public int getResult()
{
return count;
}
}
The countCharacters() function, which implements the logic of the Visitor pattern:
/**
* Counts the words in the text by using the Iterator and Visitor patterns.
*/
int countCharacters()
{
CharacterVisitor characterVisitor = new CharacterVisitor();
CharacterIterator characterIterator =
new CharacterIterator(editorController.getCompositeBuilder());
characterIterator.first();
while(!characterIterator.isDone())
{
CustomCharacter customCharacter = characterIterator.getCurrent();
customCharacter.accept(characterVisitor);
characterIterator.next();
}
return characterVisitor.getResult();
}
Note that in this example I also implement the Iterator pattern as well. I go in to more details in the case study.
You can find the complete code in this github repository, where I explain all of the design patterns mentioned in the legendary GOF book.
The visit(object) method is able to update or modify the object it visits. That said it is only able to update or modify fields or properties that are both public and not readonly on that class. Alternatively the visit method could use public methods that object exposes to update or modify the object.

"Special" AOP UseCase

I have some compiled library which contains a method like this:
public boolean foo(String userID) {
Class<?> ntSystemClass = Thread.currentThread().getContextClassLoader()
.loadClass("com.sun.security.auth.module.NTSystem");
Method getNameMethod = ntSystemClass.getMethod("getName", null);
Object ntSystem = ntSystemClass.newInstance();
String name = (String)getNameMethod.invoke(ntSystem, null);
boolean same=userID.equalsIgnoreCase(name);
if (same) {
// more work done here
} else {
// more work done here
}
}
For some rather special use case I need to ensure that the boolean same is always true.
My first approach was extending the class and overriding the method foo() but that wasn't realizable because within the method many references on other library private stuff is required.
So the next approach is using AOP. I tried a few things with AspectJ but didn't find a solution. Can anyone help we with this? As far as I understand I can't alter the boolean same directly. Is there a possibility to address the String name = (String)getNameMethod.invoke(ntSystem, null); in any way just within the library?
Let us talk straight here: Henceforth I am assuming that your "special use case" is that you want to tweak/hack user authentication, hopefully in a legal way for testing or whatever.
The main problems to be solved are:
It would be easy to just manipulate the result of your foo method (I have renamed it to isCurrentUser(String userID) to clarify its intent). But if I understand correctly, the method has side effects, i.e. it calls other methods, and you want to keep those side effects. So we have to be more careful and use a scalpel, not an axe.
There is no pointcut for local variable changes so you have to intercept the method execution or call which changes the local variable's value.
AspectJ cannot normally intercept JDK method execution() (unless you want to weave the JDK first, which is possible but out of scope here). Thus, you have to intercept the call() from your own code. I am assuming it is possible to weave into that code even if it is contained in a JAR and you do not have the sources. You can either use LTW for the target class or binary weaving for the JAR, creating a new, woven version of it.
Your method call to NTSystem.getName() is not done in a normal way but via Reflection API. Thus, you cannot just use a pointcut like call(NTSystem.getName()) because it will never be triggered. You have to intercept call(public Object Method.invoke(Object, Object...)).
Theoretically it could be that other reflective method calls are made from within isCurrentUser(..), so we have to refine our pointcut in order to only match if really NTSystem.getName() is called, not any other method.
As a convenience function, we want to be able to dynamically switch the hackingMode on and off.
Now here is a complete, compileable code sample (obviously only working on Windows just like your own code snippet):
Java class with method to be manipulated and main method for demonstration purposes:
package de.scrum_master.app;
import java.lang.reflect.Method;
import de.scrum_master.aspect.TweakAuthenticationAspect;
public class UserAuthentication {
private static final String USER_NAME_GOOD = "alexander"; // Add your own user name here
private static final String USER_NAME_BAD = "hacker";
public static boolean isCurrentUser(String userID) throws Exception {
Class<?> ntSystemClass = Thread.currentThread().getContextClassLoader()
.loadClass("com.sun.security.auth.module.NTSystem");
Method getNameMethod = ntSystemClass.getMethod("getName");
Object ntSystem = ntSystemClass.newInstance();
String currentUserID = (String) getNameMethod.invoke(ntSystem);
boolean same = userID.equalsIgnoreCase(currentUserID);
if (same) {
System.out.println("Do something (same == true)");
} else {
System.out.println("Do something (same == false)");
}
return same;
}
public static void main(String[] args) throws Exception {
testAuthentication(false);
testAuthentication(true);
}
private static void testAuthentication(boolean hackingMode) throws Exception {
TweakAuthenticationAspect.hackingMode = hackingMode;
System.out.println("Testing authentication for hackingMode == " + hackingMode);
System.out.println("Authentication result for " + USER_NAME_GOOD + ": "
+ isCurrentUser(USER_NAME_GOOD));
System.out.println("Authentication result for " + USER_NAME_BAD + ": "
+ isCurrentUser(USER_NAME_BAD));
System.out.println();
}
}
As you can see, testAuthentication(boolean hackingMode) is called twice, once with the hacking code disabled and then enabled. In both cases it tests a good/correct user name (please edit!) first and then a bad one ("hacker").
Aspect manipulating the authentication method:
package de.scrum_master.aspect;
import com.sun.security.auth.module.NTSystem;
import de.scrum_master.app.UserAuthentication;
import java.lang.reflect.Method;
public aspect TweakAuthenticationAspect {
public static boolean hackingMode = false;
pointcut reflectiveCall_NTSystem_getName(NTSystem ntSystem, Method method) :
call(public Object Method.invoke(Object, Object...)) &&
args(ntSystem, *) &&
target(method) &&
if(method.getName().equals("getName"));
pointcut cflow_isCurrentUser(String userID) :
cflow(
execution(* UserAuthentication.isCurrentUser(String)) &&
args(userID)
);
Object around(NTSystem ntSystem, Method method, String userID) :
reflectiveCall_NTSystem_getName(ntSystem, method) &&
cflow_isCurrentUser(userID) &&
if(hackingMode)
{
System.out.println("Join point: " + thisJoinPoint);
System.out.println("Given user ID: " + userID);
System.out.println("Reflectively called method: " + method);
return userID;
}
}
A few words of explanation here:
Pointcut reflectiveCall_NTSystem_getName intercepts calls to Method.invoke(..), restricting the first parameter to NTSystem type, which eliminates reflective calls to other classes. It also checks if the target method is actually getName. I.e. the pointcut checks if really NTSystem.getName()` is to be invoked.
Pointcut cflow_isCurrentUser catches joinpoints in the control flow of method UserAuthentication.isCurrentUser(..), exposing its parameter userID for later use.
The around(NTSystem ntSystem, Method method, String userID) advice combines both pointcuts with && and has access to the three named objects in its signature. In its method body we can do whatever we please with those objects, e.g. print them to the console. We could also change their state, which is not necessary in this case. The advice is activated dynamically via if(hackingMode). If you do not need this, you can remove it, it is just for convenience. Because we use an around() advice here, we can return anything instead of the original method result. In this case, we always return userID, as if the given user was the one currently logged into Windows. This effectively results in the local same variable to become always true because the call to equalsIgnoreCase(..) always returns true as well.
I could also have manipulated the result of equalsIgnoreCase(..) directly, but then local variable currentUserID would be non-equal to userID. Depending on which kinds of side effects you want, you can change this behaviour according to your preferences.
Sample output:
Testing authentication for hackingMode == false
Do something (same == true)
Authentication result for alexander: true
Do something (same == false)
Authentication result for hacker: false
Testing authentication for hackingMode == true
Join point: call(Object java.lang.reflect.Method.invoke(Object, Object[]))
Given user ID: alexander
Reflectively called method: public java.lang.String com.sun.security.auth.module.NTSystem.getName()
Do something (same == true)
Authentication result for alexander: true
Join point: call(Object java.lang.reflect.Method.invoke(Object, Object[]))
Given user ID: hacker
Reflectively called method: public java.lang.String com.sun.security.auth.module.NTSystem.getName()
Do something (same == true)
Authentication result for hacker: true
You can see in the upper part that authentication works as usual if hackingMode == false, but will always positively authenticate any given user name if hackingMode == true.

How to create an object by referring to a property

I want to create an object of a class by referring to its object, I think. I've been able to make it in C# but in Java it wont work. This is what I want to do:
controller.getDal().getStudentData().getPerson() = new Person(student.getIdNumber(), student.getName(), student.getAddress(), student.getTel());
But I get a error message saying:The left-hand side of an assignment must be a variable
How can I fix the problem? I've tried like doing like this:
register.AddStudent(controller.getDal().getStudentData().getPerson());
and then
System.out.println("Show info: " + controller.getDal().getStudentData().getPerson());
and the output I get is : Person#7cd0a5d9
Java doesn't have the Property syntax that c# does. you have to use a setter.
controller.getDal().getStudentData().setPerson(
new Person(/*blah blah blah*/)
);
if you control whatever type getStudentData returns, than you might have to make one.
public void setPerson(Person newPerson)
{
this.person = newPerson;
}
Right now you are trying to set a new person using a get method. You cannot set an object to a function. You are on the right track with your code:
register.AddStudent(controller.getDal().getStudentData().getPerson());
I do wonder however if in your code that a student and a person are the same thing. You did not provide enough code for me to test and to give you an guaranteed answer, but I would assumg that your code should be more like this:
register.AddStudent(controller.getDal().getStudent());
This way you are getting the student and then adding the student. I'm not sure what you are trying to accomplish but you should really be looking into set methods such as something like:
Person p = controller.getDal().getStudentData().getPerson();
p.setIdNumber = 0011559966
p.setAddress('123 C St');
Or even something along the lines of:
register.AddStudent(new Student("Billy", "Crystal", "123 C st"));
Anyway, if you had more code, I would be able to help you more, but that is the best I can think of without any real context
About:
System.out.println("Show info: " + controller.getDal().getStudentData().getPerson());
You must override the toString() method inside the Person class to the fields or string representation you want to see upon printing.
An example could be:
#Override
public String toString() {
return "Name: " + this.getName() + " Id Num: " + this.getIdNumber();
}

Print object information as toString() without toString()?

my Question is, is it possible to print out specific information from a class that provides no toString() method in JAVA?
There's the rub: We provide a logger for our application (using aspectJ) which prints out the specific arguments, that were given. For example:
public void addGroupMembers(Group group, String doneBy, DataListModel<User> users) {
doSomething()
}
And our Logger prints the following:
addGroupMembers called with given arguments:
Group = [id = ..... and so on]
username
DataListModel$1231 <-
We have to use the DataListModel-Class, because we're working with JSF in the background. But, as you can see, this class doesn't provide a toString method.
Our logger is written by ourself, so we can adapt that. Is it possible to simulate a toString method like: If the class doesn't provide a toString, catch all their fields, and print them?
Or is there any other way?
Thanks in advance for your help.
Greetings, Thunderhook
You could make use of ReflectionToStringBuilder. Something like:
if (object.toString().endsWith("#" + Integer.toHexString(object.hashCode())) {
// default toString()...
return ReflectionToStringBuilder.toString(object);
}
else {
return object.toString();
}
I've done this a few times using reflection. I had a generic method that was essentially dumpObject that iterated through the fields and put them to a string. This works great, but be sure to consider performance if you're calling this often - it might be better to hard code the tostring. e.g.,
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
log.info(field.getName()
+ " - " + field.getType()
+ " - " + field.get(obj));
}

Categories