1

Here is a code snippet to explain the question:

public class Demo {
    // I want to enhance this method.
    public String a() {
        return "A";
    }

    // this method is not enhanced but the internal a() invocation should be
    public String b() {
        return "-> " + a();
    }
}

I want to enhance method a(), and when method b() invoked, the enhanced a() should be executed. I guess that a CGLIB proxy takes no effort.

Besides, this enhancement action should be object-level rather than class-level.

public static void verify() {
    assert Objects.equals("-> A", new Demo().b());
    assert !Objects.equals("-> A", enhance(new Demo()).b());
}

public static Demo enhance(Demo demo) {
    throw new UnsupportedOperationException("I don't know how to do");
}

Is there a solution to resolve this problem?

BTW: sorry for my poor English, but I think my intention is described clearly.

Supplement:

Actually, my issue is:

There are exception stack traces will be logged, but some of them have sensitive data in their detailMessage field by using new XXException("sensitive"), or some IOException will print the detailed absolute path.

printStackTrace() will invoke toString() method, and toString() method will invoke getMessage() method. (You can see the JDK source code)

I want to supply a utility method to wrap/enhance the Exception, let printStackTrace() not print the sensitive data but print the stack trace for finding bugs. Then these Exception objects can be sent to log4j safely.

Tom Jerry
  • 307
  • 2
  • 10
  • Why? You can't do that, at least not in any easy and smart way. – Kayaman Mar 24 '17 at 16:13
  • 1
    _"this enhancement action should be object-level rather than class-level"_ -- Objects don't have code, only classes have code. – Jim Garrison Mar 24 '17 at 16:15
  • You want to enhance at runtime or at compile time? – Jeff Miller Mar 24 '17 at 16:54
  • Thank you all very much!I updated the question, added a supplement at the end of the question, you have any idea to solve it? @Kayaman – Tom Jerry Mar 24 '17 at 17:34
  • Isn't the real issue that you're including sensitive information in exceptions, when you shouldn't be? Your attempted workaround seems quite absurd now that we know the real problem you're trying to solve. – Kayaman Mar 24 '17 at 17:37
  • @JeffMiller Runtime! I updated the question with a supplement. – Tom Jerry Mar 24 '17 at 17:41
  • @Kayaman Yes, maybe I think about the question too odd, but my company's security checklist contains the 'DO NOT log the absolute file path to the log file', there're some I/O related Exceptions contain it, I don't want to abandon the stack trace. – Tom Jerry Mar 24 '17 at 17:54
  • Well, I suggest you talk to your colleagues about a proper solution. You're trying to go a long way to prevent a path from being logged. If I needed to do this, I'd probably look into the logging framework for mechanisms to do the redacting there, instead of trying to remove the information from the exception itself. Maybe a custom appender, if you're using Log4J. – Kayaman Mar 24 '17 at 18:28

4 Answers4

2

I don't see what's the problem with just subclassing.

class EnhancedDemo extends Demo{
    @Override
    public String a(){
        return "Enhanced A";
    }
}

You can still use this in place of a normal Demo:

public static void main(String... args) throws Exception {
    Demo myDemo = new EnhancedDemo();
    System.out.println(myDemo.b());
    //output: -> Enhanced A
}

Is this not sufficient? Can't you refactor the code where you think you need this to allow for dependency injection instead?

Imus
  • 802
  • 6
  • 11
0

As you mentioned CGLIB proxy (and java dynamic proxies) both have the same issue. When calling another method from a proxied method (in your case when b() invokes a()), the reference 'self' is used, and the proxy never comes into the picture.

As a work around you may consider passing the proxied object itself to the class, as in this SO Answer. The thread ask a similar question as you where the 'enhancement' is Spring's transaction processing proxy. After that you may use the reference to self everywhere instead of the straight forward invocation.

You may also consider, refactoring your code, so that such a case does not arise. But then that may or may not be possible depending upon your use case.

As for the latter question about enhancement action should be object-level rather than class-level java dynamic proxies work on an object level. So if you create a new object of the class and do not proxy it, it will remain the 'enchancement-free'.

Community
  • 1
  • 1
digvijay91
  • 697
  • 1
  • 6
  • 21
0

You seem to be approaching the problem incorrectly. The problem does not appear to be "how do I change junk dynamically" as much as "how do I correctly design my code to allow for dynamic change of a method's implementation". The answer is: implement a Strategy pattern for the method a functionality.

Here is an example:

public interface MethodAStrategy
{
    String methodAImplementation();
}

public class SimpleAStrategy
implements MethodAStrategy
{
    public String methodAImplementation()
    {
        return "a";
    }
}

public class EnhancedAStrategy
implements MethodAStrategy
{
    public String methodAImplementation()
    {
        return "enhanced a";
    }
}

public class EnhancedDemo
extends Demo
{
    private MethodAStrategy strategyForMethodA;
    private MethodAStrategy strategyForMethodB;

    public EnhancedDemo()
    {
        strategyForMethodA = new SimpleAStrategy();
        strategyForMethodB = new EnhancedAStrategy()
    }

    public String a()
    {
        return strategyForMethodA.methodAImplementation();
    }

    public String b()
    {
        return "->" + strategyForMethodB.methodAImplementation();
    }

    // as desired, add methods to change the strategies.
}
DwB
  • 37,124
  • 11
  • 56
  • 82
0

As your real problem is preventing certain data from being logged to file, the simplest way would be to intercept this at the logging point. Extending Log4J's FileAppender and redacting the absolute paths there should be easy enough. Then you'll just need to make sure you use the custom appender instead of the regular one, but that's easier to do than what you had in mind.

Kayaman
  • 72,141
  • 5
  • 83
  • 121