http://www.developer.com/

Back to article

Design Pattern: Proxy


August 1, 2007

A proxy is a stand-in for something or someone else. As an actor, you might hire a proxy to attend an autograph signing session. Your proxy is providing a layer between you and your fans, but can forward relevant messages as necessary. You want your proxy to behave as much like you do as possible, so that the fans believe they are interacting with you directly.

Proxies in software are similar to proxies in real life. You might create a distributed object proxy. Such a proxy is designed to make its clients think that they are directly interacting with the object, when in reality the object lives in a process on a remote machine. The proxy manages the intricacies of communicating with the distributed object while ensuring that the client remains blissfully ignorant of these details.

As you might imagine, proxies in Java are heavily dependent upon having both the proxy and the target class—the class that the proxy is "standing in" for—implement a common interface. The proxy needs to respond to all the same messages as the target, and forward them as appropriate.

In this article, I'll demonstrate use of a proxy that provides a security layer, disallowing clients with insufficient access from executing specific methods.

I've built a Portfolio class using test-driven development (TDD). The simple implementation of Portfolio provides two query methods, numberOfHoldings and sharesOf, and one update method named purchase.

Listing 1: PortfolioTest

import static org.junit.Assert.*;
import org.junit.*;

public class PortfolioTest {
   private Portfolio portfolio;

   @Before
   public void initialize() {
      portfolio = new Portfolio();
   }

   @Test
   public void containsNoHoldingsOnCreation() {
      assertEquals(0, portfolio.numberOfHoldings());
   }

   @Test
   public void storesSinglePurchaseSingleShare() {
      assertEquals(0, portfolio.sharesOf("MSFT"));
      portfolio.purchase("MSFT", 1);
      assertEquals(1, portfolio.numberOfHoldings());
      assertEquals(1, portfolio.sharesOf("MSFT"));
   }

   @Test
   public void sumsSharesForMultiplePurchasesSameSymbol() {
      portfolio.purchase("MSFT", 1);
      portfolio.purchase("MSFT", 2);
      assertEquals(1, portfolio.numberOfHoldings());
      assertEquals(3, portfolio.sharesOf("MSFT"));
   }

   @Test
   public void segregatesSharesBySymbol() {
      portfolio.purchase("MSFT", 5);
      portfolio.purchase("IBM", 10);
      assertEquals(2, portfolio.numberOfHoldings());
      assertEquals(5, portfolio.sharesOf("MSFT"));
   }

   @Test(expected=InvalidSymbolException.class)
   public void disallowsPurchaseOfNullSymbol() {
      portfolio.purchase(null, 0);
   }
}

Listing 2: Portfolio

import java.util.*;

public class Portfolio {
   private Map<String,Integer> symbols =
      new HashMap<String,Integer>();

   public int numberOfHoldings() {
      return symbols.size();
   }

   public int sharesOf(String symbol) {
      if (!symbols.containsKey(symbol))
         return 0;
      return symbols.get(symbol);
   }

   public void purchase(String symbol, int shares) {
      if (symbol == null)
         throw new InvalidSymbolException();
      symbols.put(symbol, shares + sharesOf(symbol));
   }
}

The InvalidSymbolException class is simply an empty subclass of RuntimeException.

The first step toward building a proxy is to define a common interface. I execute an Extract Implementer refactoring [Feathers2004], ending up with an interface named Portfolio and an implementation of the Portfolio interface named PortfolioImpl.

Listing 3: Introducing an interface

// Portfolio.java
public interface Portfolio {
   int numberOfHoldings();
   int sharesOf(String symbol);
   void purchase(String symbol, int shares);
}

// PortfolioImpl.java--renamed from Portfolio
import java.util.*;

public class PortfolioImpl implements Portfolio {
   ...

With the interface in place, I can start building the proxy. For now, I keep it simple, and define the proxy only to delegate to the real Portfolio.

import static org.junit.Assert.*;
import java.util.*;
import org.junit.*;

public class PortfolioGuardTest {
   private Map<String, Integer> purchases;

   Portfolio target = new Portfolio() {
      @Override
      public int numberOfHoldings() {
         return 1;
      }

      @Override
      public void purchase(String symbol, int shares) {
         purchases.put(symbol, shares);
      }

      @Override
      public int sharesOf(String symbol) {
         if (symbol.equals("symbol")) return 1;
         return 0;
      }
   };

   @Before
   public void initialize() {
      purchases = new HashMap<String, Integer>();
   }

   @Test
   public void delegatesAllMethods() {
      boolean canUpdate = true;

      Portfolio portfolio = new PortfolioGuard(canUpdate, target);
      portfolio.purchase("symbol", 1);
      assertEquals(1, purchases.size());
      assertEquals(1, purchases.get("symbol"));
      assertEquals(1, portfolio.numberOfHoldings());
      assertEquals(1, portfolio.sharesOf("symbol"));
   }
}

Rather than use PortfolioImpl, I define a stub implementation of the Portfolio interface in PortfolioGuardTest. This implementation exists solely to track whether or not appropriate methods were called, as part of the delegatesAllMethods test.

The constructor of PortfolioGuard takes two arguments: a target instance of Portfolio, and a boolean indicating whether or not updates are allowed. For now, this flag is set to true. Here's the implementation of PortfolioGuard:

public class PortfolioGuard implements Portfolio {
   private final Portfolio target;
   private final boolean canUpdate;

   public PortfolioGuard(boolean canUpdate, Portfolio target) {
      this.canUpdate = canUpdate;
      this.target = target;
   }

   @Override
   public int numberOfHoldings() {
      return target.numberOfHoldings();
   }

   @Override
   public void purchase(String symbol, int shares) {
      target.purchase(symbol, shares);
   }

   @Override
   public int sharesOf(String symbol) {
      return target.sharesOf(symbol);
   }
}

In a second test, I set the "can update" flag to false, and then verify that calling the purchase method generates an exception.

@Test(expected=RuntimeException.class)
public void prohibitsMutatorMethodsWhenUpdateRestricted() {
   boolean canUpdate = false;
   Portfolio portfolio = new PortfolioGuard(canUpdate, target);
   portfolio.purchase("symbol", 1);
}

After demonstrating test failure, I add a couple lines to PortfolioGuard in order to get it to pass:

@Override
public void purchase(String symbol, int shares) {
   if (!canUpdate)
      throw new RuntimeException();
   target.purchase(symbol, shares);
}

The final piece of the puzzle is to make sure that clients interact with a PortfolioGuard instance and not a real PortfolioImpl instance. I can do that by introducing a factory and telling client developers to use the factory to obtain Portfolio objects.

import static org.junit.Assert.*;
import org.junit.*;

public class PortfolioFactoryTest {
   @Test
   public void createWithUpdateAccess() {
      User user = User.create("x");
      Portfolio portfolio = PortfolioFactory.create(user);
      portfolio.purchase("x", 1);
      assertEquals(1, portfolio.numberOfHoldings());
   }

   @Test(expected=RuntimeException.class)
   public void createWithReadOnlyAccess() {
      User user = User.createReadOnly("x");
      Portfolio portfolio = PortfolioFactory.create(user);
      portfolio.purchase("x", 1);
   }
}

There's not much to the factory:

public class PortfolioFactory {
   public static Portfolio create(User user) {
      return new PortfolioGuard(user.canUpdate(),
                                new PortfolioImpl());
   }

}

PortfolioFactory requires a User object. The User class encapsulates a user ID and information about whether or not a user has update access:

import static org.junit.Assert.*;
import org.junit.*;

public class UserTest {
   @Test
   public void createReadOnly() {
      User user = User.createReadOnly("jlangr");
      assertFalse(user.canUpdate());
      assertEquals("jlangr", user.getName());
   }

   @Test
   public void createWithUpdateAccess() {
      User user = User.create("jlangr");
      assertTrue(user.canUpdate());
   }
}
public class User {
   private final boolean canUpdate;
   private final String name;

   public User(String name, boolean canUpdate) {
      this.name = name;
      this.canUpdate = canUpdate;
   }

   public static User create(String name) {
      return new User(name, true);
   }

   public static User createReadOnly(String name) {
      return new User(name, false);
   }

   public boolean canUpdate() {
      return canUpdate;
   }

   public String getName() {
      return name;
   }
}

The proxy implementation is complete: A PortfolioGuard stands in for a PortfolioImpl, disallowing clients from making purchases when they don't have the proper access rights. A UML class diagram representing this design appears in Figure 1.

A Better Implementation

My simple implementation of the proxy pattern isn't as good as it could be. First, the maintenance work will increase as the Portfolio interface changes. Not only do I need to provide implementations in the PortfolioImpl class, but I also need to provide corresponding delegator methods in PortfolioGuard. Second, I need to track which methods need to be protected and write appropriate guard clauses in the PortfolioGuard class. It'd be nice if I could tag methods directly within the Portfolio interface.

Fortunately, Java provides mechanisms that can help solve both problems. The dynamic proxy mechanism introduced in Java 1.3, although a bit ugly, eliminates the need to continually update PortfolioGuard as Portfolio changes. And annotations, introduced in Java 5, gives me a way of directly tagging "mutator" methods—methods that update Portfolio object state. The changed classes appear in Listing 4. For details on these two Java features, refer to Sun's Java pages, or better, search the web!

Listing 4: Using dynamic proxy

// MutatorTest:
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import org.junit.Test;

public class MutatorTest {
   @Test
   public void nonpresence() throws SecurityException,
      NoSuchMethodException {
      Method method = X.class.getDeclaredMethod("readOnlyMethod");
      assertFalse(method.isAnnotationPresent(Mutator.class));
   }

   @Test
   public void presence() throws SecurityException,
      NoSuchMethodException {
      Method method = X.class.getDeclaredMethod("mutatorMethod");
      assertTrue(method.isAnnotationPresent(Mutator.class));
   }

   class X {
      public void readOnlyMethod() {}
      @Mutator public void mutatorMethod() {}
   }
}

// Mutator:
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Mutator {
}

// Portfolio:
public interface Portfolio {
   int numberOfHoldings();
   int sharesOf(String symbol);
   @Mutator void purchase(String symbol, int shares);
}

// PortfolioGuardTest:
...
public class PortfolioGuardTest {
   ...
   @Test(expected = RuntimeException.class)
   public void prohibitsMutatorMethodsWhenCanUpdateFalse() {
      boolean canUpdate = false;
      // note how I must now construct portfolio proxies:
      Portfolio portfolio = PortfolioGuard.create(canUpdate, target);
      portfolio.purchase("symbol", 1);
   }
}

// PortfolioGuard:
import java.lang.reflect.*;

public class PortfolioGuard implements InvocationHandler {
   public static Portfolio create(boolean canUpdate,
      Portfolio target) {
      PortfolioGuard guardedPortfolio =
         new PortfolioGuard(canUpdate, target);
      return (Portfolio)Proxy.newProxyInstance(
            Portfolio.class.getClassLoader(),
               new Class[] {Portfolio.class}, guardedPortfolio);
   }

   private boolean canUpdate;
   private Object target;

   public PortfolioGuard(boolean canUpdate, Portfolio target) {
      this.canUpdate = canUpdate;
      this.target = target;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable {
      if (method.isAnnotationPresent(Mutator.class) && !canUpdate)
         throw new RuntimeException();
      return method.invoke(target, args);
   }
}



Click here for a larger image.

Figure 1: The proxy pattern.

Reference

[Gamma] Gamma, E., et. al. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional, 1995.

About the Author

Jeff Langr is a veteran software developer celebrating his 25th year of professional software development. He's authored two books and dozens of published articles on software development, including Agile Java: Crafting Code With Test-Driven Development (Prentice Hall) in 2005. You can find out more about Jeff at his site, http://langrsoft.com, or you can contact him via email at jeff at langrsoft.com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date