2001-01-18 TJSN [Issue 005] - Dynamic Proxies - Short Tutorial

2001-01-18 TJSN [Issue 005] - Dynamic Proxies - Short Tutorial

Author: Dr. Christoph G. Jung

If you are not already subscribed to this newsletter, please send an email to subscribe@javaspecialists.co.za. Be warned that if you are a beginner in Java, you will at times struggle to keep up. The archive of past newsletters is kept at http://www.javaspecialists.co.za

Welcome to our fifth "The Java(tm) Specialists' Newsletter", published by Maximum Solutions, the company who may not be good at Java(tm), but Java is all we do, so we call ourselves "Java Specialists". Thank you for not unsubscribing in hordes (hmmm, I have not told you HOW to unsubscribe, so maybe that's why ;-)

Isn't it funny how employment agencies ask you ALL the things you know, like "which word processor have you used?" and "what version of Turbo Pascal do you know?" What relevance is that to today's newsletter? None.

Anyway, we've had an interesting week sorting out small Java problems and fixing bugs in my software. Many thanks to Dr. Christoph for writing this week's newsletter, which I really enjoyed reading. For this I owe you an Esplendido! Christoph started writing Java code at the end of 1999 for a very innovative ERP company in Germany called infor Business Solutions AG, and has quickly become a real fundi at writing application server code. Prior to that, he did a doctorate in Computer Science with emphasis on Robotics and Artificial Intelligence. I don't know where Java comes in, but a PhD is general education that is useful for many spheres of life, and it does not really matter what your topic was. Believe me, my PhD topic was even less useful ;-)

Anyway, enough from me, here is a "Short Tutorial to the Java2 platform Dynamic Proxy Facilities" ....... I wish we had known about this before we started pasting access control onto our 570'000 line application ......

Short Tutorial to the Java2(TM) platform Dynamic Proxy Facilities

C.G.Jung (schorsch@javaspecialists.co.za)

A Bad Example

One the most useful design patterns when it comes to building extensible frameworks turns out to be the PROXY pattern. For example, imagine your notoriously paniqued project manager insisting on your introducing new security features into your companies existing business model:

  /**
   * A Class representing a single salary
   */
  class Salary {
    Employee employee;
    long amount;
  
    public long getAmount() {
      return amount;
    }
  }

Your straightforward approach to shield the objects from exporting sensible information, such as exorbitant management salaries, to ordinary users, such as you humble worm, would be to introduce a dedicated SecurityChecker (this is not a java.lang.Security tutorial, so please excuse the following very naive code):

  /**
   * Realises The Systems Security Policies
   */
  class SecurityChecker {
    java.security.Permission manyBucksPermission;
  
    long check(long amount) throws SecurityException {
      if(amount>1000000)
        SecurityManager.checkPermission(manyBucksPermission);
      return amount;
    }
  }
  /**
   * A Modified Salary that is "Too-Much-Bucks"-Aware
   */
  
  class Salary {
    Employee employee;
    long amount;
    // ugly reference
    static SecurityChecker sc;
  
    public long getAmount() {
      // isnt this the wrong place for doing that?
      return sc.check(amount);
    }
  }

Now your project manager is happy because of being able to ask for his next salary increase. But, you (and your colleague that is the application but not the framework specialist) will quite likely curse him three times when he returns with next terms project plan that includes

  1. heavily extending the business logic (amount will no more be stored immediately, but computed by a very complex, profile-oriented function, an additional int getAmount() is needed in Salary, an Order should be implemented, etc.)
  2. expanding the successfully introduced security policies (maybe a check(Customer customer) should be added that restricts access to data associated with particular "high-sensitive" customers).

The reason for your deadly wishes is of course that in the above solution, we have heavily mixed up application data and methods (such as amount and long getAmount() ) with generic access control (such as check(long amount)) Extending and updating the 200 classes with 1000 methods of application logic involves understanding of when and how to correctly apply the available security checks. Because only you as the framework pro are able to do that, you will end up with a quite suboptimal division of responsibility and code.

The Proxy Solution

In order to free your colleague from the heavy burden of the security framework and enable him to concentrate on his business, the better choice is hence to leave his Salary class alone, but rather provide him with an inherited PROXY class that intercepts any getAmount() calls to interface the security framework:
  /**
   * proxy that behaves as a salary
   * and that performs a security check
   * before delegation
   */
  
  class SalarySecurityProxy extends Salary {
    // this is not application, hence the reference is ok
    static SecurityChecker sc;
    // the object to which we delegate the calls to
    Salary realObject;
  
    public long getAmount() {
      // framework call after delegation
      return sc.check(realObject.getAmount());
    }
  }

Now, your collegue is free to change and extend the application logic to realise project goal a (e.g., manipulating amount and getAmount()), while you can add additional independently further proxies and intercepting/ delegation calls to pursue project goal b. Strike.

If your initial application design has been smart by using mucho, mucho interfaces (you know that you should do that anyway, donīt you?), PROXY can be even made compatible with complex inheritances:

  /** use interfaces everywhere in your app-logic */
  interface Salary {
  ...
  }
  /** and provide proper implementations */
  class SalaryImpl implements Salary{
  ...
  }
  /** as well as framework proxies */
  class SalarySecurityProxy implements Salary{
  ...
  }
  /** when you extend your model ... */
  interface ManagementSalary extends Salary {
  ...
  }
  /** ... afterwards ... */
  class ManagementSalaryImpl extends SalaryImpl {
  ...
  }
  /** ... it's very easy */
  class ManagementSalarySecurityProxy extends SalarySecurityProxy {
  ...
  }

Double Strike.

The possibilities of the proxy are nearly endless and range from transparent SECURITY over LAZY RETRIEVAL, POOLING, CACHING, and TRANSACTIONAL MARKUP up to DISTRIBUTION (remember RMI stubs?). It is highly useful to combine such differently focused proxies in a CHAIN OF RESPONSIBILITY to a complex, at the same time extensible framework, e.g., SalarySecurityProxy (performs security checks) --> SalaryPersistenceProxy (retrieves the "real" salary from a database) --> Salary

Often, PROXY is used in combination with the FACTORY pattern in order to allow the transparent insertion of proxies and chains into the application logic (your colleague wouldīnt be bothered by their construction at all ... no more endless discussions ... no more fruitless explanations ... must be like heaven).

Why Dynamic Proxies are needed

However, the main drawback of PROXY is that for each application class, each application method, and each additional framework functionality, you have to implement and maintain (sic!) a dedicated interceptor class/method. Depending on the output of your colleague, this can be quite a pain in the ass ... (BTW: Iīm sure that some of you already collided with the winding RMIC procedure which actually is an automated proxy-generator and -compiler not to speak of its ubiquitious stub-deployment problem)

If reminding the mostly general nature of framework code, you ask yourself whether there is not a better solution to this issue. Actually, there is. It is called DYNAMIC PROXIES and one of the wonderful invents that came to your harddisk with your JRE/JDK1.3 installation.

DYNAMIC PROXIES heavily rely on the already matured reflection capabilities of the Java2 platform. They separate the "interception logic", that is the part of simulating an arbitrary interface, from the "invocation logic", that is the part of calling the framework before or after delegating any method call to the proper application target.

The "invocation logic", such as to connect to a method-based security checker, is realised on purpose by implementing the freshly introduced java.lang.reflect.InvocationHandler interface (more specifically, its reflection-based invoke-Method):

  /**
   * a generic, method-based security handler
   * that is not only useful for Salaries
   */

  class SecurityHandler extends InvocationHandler {
    static SecurityChecker sc;
    final Object realObject;
  
    /** contructor accepts the real subject */
    public SecurityHandler(Object real) {
      realObject=real;
    }
  
    /** a generic, reflection-based secure invocation */
    public Object invoke(Object target,
        java.lang.reflect.Method method, Object[] arguments)
        throws Throwable {
      try{
        // call framework and then reflect the app-logic
        return sc.check(method).invoke(realObject,arguments);
      } catch(java.lang.reflect.InvocationTargetException e) {
        // reconvert nested application exceptions
        throw e.getTargetException();
      }
    }
  }

The "interception logic" is already hardwired into the 1.3 vm and the new java.lang.reflect.Proxy class. All it requires to combine it with the above SecurityHandler code is to call the static:

  ManagementSalary salary;
  
  ManagementSalary smellsLikeSalary=(ManagementSalary)
    java.lang.reflect.Proxy.newProxyInstance(
      Salary.class.getClassLoader(),
      new Class[] {ManagementSalary.class},
      new SecurityHandler(salary));

The thus constructed smellsLikeSalary object now behaves like an implementation of ManagementSalary. In fact, it *IS* an implementation of ManagementSalary whose class has been dynamically constructed at runtime, hence dynamically. You can inspect it (smellsLikeSalary instanceof ManagementSalary), cast it ((Salary) smellsLikeSalary) and call methods on it (smellsLikeSalary.getAmount()). Method calls will in turn be dispatched to the invoke method of the given invocation handler (where in our example, target points to smellsLikeSalary, method points to the internal Salary.getAmount() representation, and the arguments array is empty). Return values and exceptions will be automatically converted/casted into the compatible types as given in the interface signature. Thatīs it.

(No, actually thatīs not: java.lang.reflect.Proxy has a lot more advanced features and there are some tricky considerations when building proxies for multiple/conflicting interfaces - but thatīs too much for now, please refer to the API documentation for these issues.)

If PROXY has broad applicability, this holds for DYNAMIC PROXIES even more. For example, you could imagine a PersistenceHandler that hides JDBC-access to a row in a relational database table without requiring any additional object instances. For example, you could think of a JRMPRemoteHandler that implements stub-less (HHHOOOORRRAAAYYYY ... no more RMIC) RMI-access to a remote object via the TCP/IP-based Java Remote Method Protocol.

The latter usage is especially supported by DYNAMIC PROXIES, such as smellsLikeSalary actually being serializable, if the tied handler classes are. Under http://www.dreambean.com/download/rickard/SmartWorld-1.2.zip you will find executable code and documentation that extends SUN-JRMP1.2 with suitable handlers that can be distributed through naming services, files, etc. and that can be straightforwardly enriched by load-balancing, failover, clustering, and connection-pooling features.

The first (Open Source!) EJB product that uses these handlers instead of remote stubs is jBoss whose complementary server container design is indeed realised as flexible CHAIN OF RESPONSIBILITY ... Long live the (DYNAMIC) PROXIES!


Thanks for that, Christoph...

Next week will be a bit shorter and I will show you how to put method bodies in Java interfaces, which you should never need to do, IF the framework you are working within is perfect. In a less-than-perfect world such a technique can be useful, as fellow fish-chaser Niko Brummer told me. So, hold your breath until next week.

Regards

Heinz and Christoph


(C)opyright Maximum Solutions, South Africa

Reprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire email with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa). Please contact Maximum Solutions for more information.

Java is a trademark or registered trademark of Sun Microsystems, Inc. in the United States and other countries. Maximum Solutions is independent of Sun Microsystems, Inc.