1

I'm trying to solve a common problem of logging each method that calls 3rd party operation and I don't understand how to scale it up. Current implementation:

public class ElasticsearchClient {

    RestHighLevelClient client = new RestHighLevelClient(
            RestClient.builder(
                new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")));

    public void createIndex() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest("twitter");
        client.indices().create(request, RequestOptions.DEFAULT);
    }
}

public class ElasticsearchProxy {

    ElasticsearchClient client = new ElasticsearchClient();
    Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());

    public void createIndex() throws IOException {
        logger.info("Calling to method " + this.getClass().getEnclosingMethod().getName() + " in ES");
        client.createIndex();
    }
}

As you can see now I only have 1 method - createIndex() but what if I have multiple methods - reIndex(), deleteIndex(), getIndex() and many more, for each of those methods I need to do:

    public void reIndex() throws IOException {
        logger.info("Calling to method " + this.getClass().getEnclosingMethod().getName() + " in ES");
        client.reIndex();
    }

    public void deleteIndex() throws IOException {
        logger.info("Calling to method " + this.getClass().getEnclosingMethod().getName() + " in ES");
        client.deleteIndex();
    }

    public void getIndex() throws IOException {
        logger.info("Calling to method " + this.getClass().getEnclosingMethod().getName() + " in ES");
        client.getIndex();
}

I find it to be a crazy code duplication and it feels that there must be another way to do it so:

  1. I will not repeat the logger.info(...) line
  2. I will not have to copy each method in the client class to be in the proxy class as well, I would like to have an option to add a method in the client class that will be automatically exposed in the proxy class but with the log message.

Thanks

3
  • 1
    I was wondering why you need two classes (client and proxy) here?
    – lennon310
    Jan 20, 2021 at 21:06
  • I dont want to put the logging code in the client class. I want the client class to have only business related code, I want it to be clean.
    – T.S
    Jan 20, 2021 at 23:07
  • Even if I put the logging in the client class, I need to repeat the call to the logger in each method which is a code duplication and error prone because someone can forget to call the logger. Having each method start with call to logger is code duplication, as I said, there must be a better way
    – T.S
    Jan 20, 2021 at 23:17

1 Answer 1

2

Use dynamic proxies.

In Java the first step is to create an interface which contains the methods you want to log and forward.

interface If {
    void originalMethod(String s);
}

Let the target class implement that interface.

class Original implements If {
    public void originalMethod(String s) {
        System.out.println(s);
    }
}

Then write an InvocationHandler which logs some text and calls the given Method on an instance of your choice (the business logic).

class Handler implements InvocationHandler {
    private final If original;

    public Handler(If original) {
        this.original = original;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        System.out.println("BEFORE");
        Object result = method.invoke(original, args);
        System.out.println("AFTER");
        return result;
    }
}

To get an object having the desired behavior you then use Proxy.newProxyInstance.

Original original = new Original();
Handler handler = new Handler(original);
If f = (If) Proxy.newProxyInstance(
                If.class.getClassLoader(),
                new Class[] { If.class },
                handler);
f.originalMethod("Hello");

Code in this example was taken (and slightly modified in order to return results from the handler which is not necessary here but in general it is) from here: https://dzone.com/articles/java-dynamic-proxy

1
  • Thank you very much. The solution is great and thanks for the reference to the article. This is exactly what I needed, a simple and scalable solution :)
    – T.S
    Jan 23, 2021 at 13:30

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.