0

We are working with mvc design pattern, where all the data is stored under map.

I want to iterate over all the classes in the system and for each to check what the method is putting on the map and what does the method get from the map.

For example for the next code:

private void myFunc()
{
Object obj = model.get("mykey");
Object obj2 = model.get("mykey2");
.....
model.put("mykey3", "aaa");
}

I want to know that in this function we have 2 gets: mykey and mykey2 and 1 put: mykey3

How can I do it with the code.

Thanks.

2
  • You might do that with the ASM byte code library. Commented Jul 2, 2018 at 8:55
  • You should really, really rethink your software design. Your question basically is a variant of “How can I find out what my software is doing?” and the root of your problems is already summarized in your question at the place where you say: “…*where all the data is stored under map*”. Using magic String literals as keys is adding fuel to the flames. If the keys had a dedicated type whose values were stored in variables that need to be accessed by the code using a particular key, you IDE could find all of these accesses within seconds. Commented Jul 5, 2018 at 8:17

2 Answers 2

2

You tagged this with "reflection", but that will not work. Reflection only allows you to inspect "signatures". You can use it to identify the methods of a class, and the arguments of the methods.

It absolutely doesn't help you to identify what each method is doing.

In order to find out about that, you would need to either parse the java source code side, or byte code classes. As in: write code that reads that content, and understands "enough" of it to find such places. Which is a very challenging effort. And of course: it is very easy to bypass all such "scanner" code, by doing things such as:

List<String> keysToUpdate = Arrays.asList("key1", "key2");
for (String key : keysToUpdate) { 
  ... does something about each key

Bang. How would you ever write code that reliable finds the keys for that? When you found that code, now imagine that the list isn't instantiated there, but far away, and past as argument? When you figured how to solve that, now consider code that uses reflection to acquire the model object, and calls method on that. See? For any "scanner" that you write down, there will be ways to make that fail.

Thus the real answer is that you are already going down the wrong rabbit hole:

You should never have written:

Object obj = model.get("mykey");

but something like

 Object obj = model.get(SOME_CONSTANT_FOR_KEY_X);

Meaning: there is no good way to control such stuff. The best you can do is to make sure that all keys are constants, coming from a central place. Because then you can at least go in, and for each key in that list of constants, you can have your IDE tell you about their usage.

Sign up to request clarification or add additional context in comments.

4 Comments

I did use constant just didn't show it in the example to make it simple. I know I can find all constants with IDE, but it doesn't help me, since we are using xml files that also refer to the map fields. I didn't wrote the code, so no - I can't change it. With the system I am working now, it will help me a lot if I do find a way. I am looking for a way do it, not for a way not to do it..
I explained about the way to do it: you need to look into ways to scan your code base, either java or class files. That is a complicated, advanced undertaking - but don't expect to receive (much more) detail than that here. We help you solving problems. Your problem is huge, and needs to be broken up into its many small pieces. We can't help with these activities, that is something that you need to do yourself. We are talking about a serious amount of work here, don't expect that people do that for you. You get some guidance here, and that is what was delivered already.
well, could be someone knows about a package that does something similar etc.
Then you are asking for a library, which renders your question off topic ... and again, that is my point: there is most likely no library exactly doing what you need. You will have to go in and do a lot of the work yourself. That is my message here: this is a lot of work, and you are the one who needs to do it ;-(
0

NOTES

  • I assumed that your situation is complicated enough that simple or advanced text search in codebase doesn't help you.
  • This is a hack not a generic solution, designed only for testing and diagnosis purposes.
  • To use this hack, you must be able to change your code and replace the actual model with the proxy instance while you're testing/diagnosing. If you can't do this, then you have to use an even more advanced hack, i.e. byte-code engineering with BCEL, ASM, etc.
  • Dynamic proxies have drawbacks on code performance, therefore not an ideal choice for production mode.
  • Using map for storing model is not a good idea. Instead a well-defined type system, i.e. Java classes, should be used.

A general design pattern for a problem like this is proxy. An intermediate object between your actual model and the caller that can intercept the calls, collect statistics, or even interfere with the original call. The proxied model ultimately sends everything to the actual model.

An obvious proxy is to simply wrap the actual model into another map, e.g.

public class MapProxy<K, V> implements Map<K, V> {

   public MapProxy(final Map<K, V> actual) {
   }

   // implement ALL methods and redirect them to the actual model

}

Now, reflection doesn't help you with this directly, but can help with implementing a proxy faster using dynamic proxies (Dynamic Proxy Classes), e.g.

    @SuppressWarnings("unchecked")
    private Map<String, Object> proxy(final Map<String, Object> model) {

        final InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // Collect usage stats or intervene
                return method.invoke(model, args);
            }
        };

        return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
            new Class<?>[] { Map.class }, handler);
    }

NOTE: Either case you need to be able to replace the actual model with the proxied model at least for the duration of your test.

With another trick, you can find out who called which method of your model. Simply by accessing Thread.currentThread().getStackTrace() and retrieving the appropriate element.

Now puting all the pieces together:

InvocationLog.java

public final class InvocationLog {

    private Method method;
    private Object[] arguments;
    private StackTraceElement caller;

    public InvocationLog(Method method, Object[] arguments, StackTraceElement caller) {
        this.method = method;
        this.arguments = arguments;
        this.caller = caller;
    }

    public Method getMethod() { return this.method; }

    public Object[] getArguments() { return this.arguments; }

    public StackTraceElement getCaller() { return this.caller; }

    @Override
    public String toString() {
        return String.format("%s (%s): %s", 
            method == null ? "<init>" : method.getName(), 
                arguments == null ? "" : Arrays.toString(arguments), 
                    caller == null ? "" : caller.toString());
    }

}

ModelWatch.java

public final class ModelWatch  {

    private final Map<String, Object> modelProxy;
    private final List<InvocationLog> logs = new ArrayList<>();

    public ModelWatch(final Map<String, Object> model) {
        modelProxy = proxy(model);
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> proxy(final Map<String, Object> model) {

        final InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                log(method, args, Thread.currentThread().getStackTrace());
                return method.invoke(model, args);
            }
        };

        return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
            new Class<?>[] { Map.class }, handler);
    }

    private void log(Method method, Object[] arguments, StackTraceElement[] stack) {
        logs.add(new InvocationLog(method, arguments, stack[3]));
        // 0: Thread.getStackTrace
        // 1: InvocationHandler.invoke
        // 2: <Proxy>
        // 3: <Caller>
    }

    public Map<String, Object> getModelProxy() { return modelProxy; }

    public List<InvocationLog> getLogs() { return logs; }

}

To put it in use:

private Map<String, Object> actualModel = new HashMap<String, Object>();
private ModelWatch modelWatch = new ModelWatch(model);
private Map<String, Object> model = modelWatch.getModelProxy(); 

// Calls to model ...

modelWatch.getLogs() // Retrieve model activity

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.