Proxy Design Pattern with Java

To make code flexible and easy to maintain we must find a way to encapsulate and not repeat the code. What if we need to do a specific treatment every time we get an Exception? Would we repeat this code in the entire application? We could, but imagine how bad the maintenance would be. It would be much easier to use the power of the Proxy pattern. We can use Reflections in just one place and make our code extremely flexible.

A very simple example of Proxy is, for example, when the company you work for wants to block certain websites. They use Proxy to intercept non-work related sites.

proxy_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Interface: Nothing special here, it’s just the interface to invoke the Service.

public interface ContractProductService {
  public String contractProduct(Long idProduct);
}

2 – Specific Exception: Exception to be treated in the Proxy class.

public class InvalidProductException extends RuntimeException {

  private static final long serialVersionUID = -5415448783266469523L;

  @Override
  public String getMessage() {
    return "Invalid product.";
  }
}

3 – Service implementation: It’s a simulation of a product contract and a very simple verification to see how we can use the Proxy pattern.

public class ContractProductServiceImpl implements ContractProductService {

  public String contractProduct(Long idProduct) {
    if (idProduct < 0) {
      throw new InvalidProductException();
    }

    return "Product contracted!";
  }

}

4 – Proxy: This is the main class where we intercept the method calls. The first important thing we are doing is the InvocationHandler interface implementation.

There are two crucial methods we use to intercept the methods invocations.

Object newInstance(Object obj): We are using the Proxy class from the Reflection API. When we instantiate any class by this method, it enables us to always intercept the method invocation.

Object invoke(Object proxy, Method m, Object[] args): It’s the method that will intercept the methods invocations when we use the method above. We are manipulating the actions if we get any Exception by any method invocation. We are simply using a JOptionPane to show the message that is thrown by any method invocation.

public class SecurityProxy implements InvocationHandler {

  private Object obj;

  private SecurityProxy(Object obj) {
    this.obj = obj;
  }

  public static Object newInstance(Object obj) {
    return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
           .getClassLoader(), obj.getClass().getInterfaces(), new SecurityProxy(obj));
  }

  @Override
  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    Object result;
    try {
      result = m.invoke(obj, args);
    } catch (InvocationTargetException e) {
      JOptionPane.showMessageDialog(null,
      e.getTargetException().getMessage());
      throw e.getTargetException();
    } catch (Exception exception) {
      throw new RuntimeException(exception);
    }
    return result;
  }

}

5 – Unit Tests: Let’s check out the use of the Proxy pattern now.

void proxyInvalidProductTest(): In this test we go through the InvalidProductException path. The first thing to understand is that we are creating the instance from the object by the newInstance method from the SecurityProxy class.

This is crucial because we need to get the instance from the SecurityProxy class to make the Proxy invocation work. Once we get the instance from the newInstance method then every method invocation will be intercepted.

void proxyValidProductTest(): It’s pretty similar to the method above. The method will also be intercepted but the difference is that we are passing a valid id to contract the product.

public class ProxyTest {

  public static final long INVALID_PRODUCT_ID = -1;
  public static final long VALID_PRODUCT_ID = 1;

  @Test(expected=InvalidProductException.class)
  public void proxyInvalidProductTest() {
    ContractProductService service = (ContractProductService) SecurityProxy.newInstance( 
                                        new ContractProductServiceImpl());

    service.contractProduct(INVALID_PRODUCT_ID);
  }

  @Test
  public void proxyValidProductTest() {
    ContractProductService service = (ContractProductService) SecurityProxy.newInstance(
                                       new ContractProductServiceImpl());
    String result = service.contractProduct(VALID_PRODUCT_ID);
    Assert.assertEquals("Product contracted!", result);
  }
}

Summary of actions:

  1. Created a simple Service to intercept the method.
  2. Created the Proxy class.
  3. Created the newInstance method in the Proxy class.
  4. Created the invoke method that does the action in the intercepted methods.
  5. Created the instance from the Service thought the newInstance method.
  6. Intercepted and implemented the actions in the invoke method from the Proxy.

To practice the Proxy pattern you can create another rule in the invoke method or you can create another Service implementation. It’s crucial to practice. Use TDD for this example!

Written by
Rafael del Nero
Join the discussion