Pulling the rug from under your feet while keeping standing – Using the Hot Swappable Target Source in Spring AOP

0

Spring AOP offers a wealth of new options in programming as well as designing Java applications. A somewhat more advanced feature is the Hot Swappable Target Source. The concept of a hot swappable target source is linked to the use of proxies instead of concrete object implementations, which is the heart of standard, run-time JDK based AOP. To advise an object with aspects, such as described in my previous post Getting into Spring AOP – Implementing simple business logic on top of Domain Objects using Aspect Oriented Programming, a proxy is created. This proxy intercepts method calls intended for the underlying target object and applies aspects for all specified pointcuts. Usually the wrapped target object still gets called somewhere in the middle of executing all the aspects that were advised. Note that the code using the proxy is not aware of the fact that it is not using a 'normal' implementation of the interface it is programmed against but instead a proxy. It does not matter for the code; only when you ask for the myobject.getClass() will get quite another classname than you would expect. However myobject instanceof interface will still result in true. Suppose we have advised an object implementing the Employee interface. The advise adds some validation logic that is executed just prior to invoking the set-methods on the Employee interface (for example to ensure that Salesmen do not get paid too much and that no employee is called John Doe). Since the code using the object is now in reality using a proxy that itself refers a 'real' EmployeeImpl object (EmployeeImpl is a class that implements the Employee interface), it should theoretically be possible to swap one EmployeeImpl currently use as proxy target with another one. Without the program noticing it, it would all of a sudden deal with a completely new underlying object. We will first show that such a thing is indeed possible – and in a fairly simple manner, and then we will discuss why you might one to do something like that.The crux of the hot-swappability lies in the fact that we can instruct the Spring AOP ProxyFactory, that we use to return proxied, advised objects, to not use a direct target object

    ProxyFactory pf = new ProxyFactory();
    pf.setTarget(emp);

but instead use a TargetSource in between:

    HotSwappableTargetSource swapper = new HotSwappableTargetSource(emp);
    pf.setTargetSource(swapper);

From the Spring documentation:

Using TargetSources – Spring offers the concept of a TargetSource, expressed in the org.springframework.aop.TargetSource interface. This interface is responsible for returning the "target object" implementing the joinpoint. The TargetSource implementation is asked for a target instance each time the AOP proxy handles a method invocation. Developers using Spring AOP don't normally need to work directly with TargetSources, but this provides a powerful means of supporting pooling, hot swappable and other sophisticated targets. For example, a pooling TargetSource can return a different target instance for each invocation, using a pool to manage instances. If you do not specify a TargetSource, a default implementation is used that wraps a local object. The same target is returned for each invocation (as you would expect). Let's look at the standard target sources provided with Spring, and how you can use them. When using a custom target source, your target will usually need to be a prototype rather than a singleton bean definition. This allows Spring to create a new target instance when required. 5.11.1. Hot swappable target sources The org.springframework.aop.target.HotSwappableTargetSource exists to allow the target of an AOP proxy to be switched while allowing callers to keep their references to it. Changing the target source's target takes effect immediately. The HotSwappableTargetSource is threadsafe. Clients who hold a reference to that bean will be unaware of the change, but will immediately start hitting the new target. Of course any TargetSource can be used in conjunction with arbitrary advice.

A simple application of the Hot Swappable Target Source

The next program creates two objects implementing the Employee interface: a concrete EmployeeImpl emp and an advised, proxied Employee scott. The advised object has some business logic implemented through an Advisor, the EmployeeValidator that intercepts method calls through the MethodBeforeAdvice interface that it implements. The program sets some properties on both objects. Then it hot swaps the advised object with the directly created EmployeeImpl, that by the way violates the business logic. When we print out the properties from the advised object scott, we now see the values we set on the concrete implementation emp. No business logic is fired at this time. When we change properties on the advised object scott, that now refers to the EmployeeImpl as the emp variable, we will see how the advise is still in place and will be applied to scott i.e. emp i.e. the underlying EmployeeImpl.

package nl.amis.spring.trial;
import java.util.HashMap;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.HotSwappableTargetSource;
//
public class HotSwappeableTargets  {
  public HotSwappeableTargets() {
  }
  //
  HashMap swapperCache = new HashMap(10);
  //
  public Employee getEmployee() {
    Employee emp = new EmployeeImpl();
    ProxyFactory pf = new ProxyFactory();
    //
    // instead of setting the emp reference directly as target for the proxy,
    // we intervene with an additional HotSwappableTargetSource. The TargetSource
    // implementation is asked for a target instance each time the AOP proxy handles a method invocation.
    // This essentially means that between invocations we can change the target
    // instance set on the TargetSource, so that subsequent TargetSource inspections
    // by the AOP Proxy Handler result in a different target object.
    HotSwappableTargetSource swapper = new HotSwappableTargetSource(emp);
    pf.setTargetSource(swapper);
    pf.setInterfaces(new Class[]{Employee.class});
    pf.addAdvice(new EmployeeValidator());
    //
    // add the swapper to a swapperCache; this allows us to later on retrieve
    // the swapper using a reference to the proxy, so that we can swap the POJO
    // underlying the proxy; note that pf.getProxy() is the reference that
    // consumers of this getEmployee() method will use instead of a direct
    // reference to an EmployeeImpl object.
    swapperCache.put(pf.getProxy(),swapper);
    return (Employee)pf.getProxy();
  }
  //
  /**
   * This method will replace the reference to oldEmployee with a reference to newEmployee.
   * Anyone who held a reference to oldEmployee will still have a valid reference, as we
   * only swap the target for the proxy. All references to oldEmployee are in fact references
   * to a proxy for the oldEmployee. By changing the target for this proxy, all references
   * remain in tact and indirectly now reference the newEmployee
   *
   * @param newEmployee
   * @param oldEmployee
   */
  private void swap(Employee oldEmployee, Employee newEmployee) {
     HotSwappableTargetSource swapper = (HotSwappableTargetSource)swapperCache.get(oldEmployee);
     Object oldTarget = swapper.swap(newEmployee);
  }
  //
  public static void main(String[] args) {
    HotSwappeableTargets mySecondAOP = new HotSwappeableTargets();
    // initialize a normal, non-AOPed bean emp:
    Employee emp = new EmployeeImpl();
    emp.setFirstName("John");
    emp.setLastName("Doe");
    emp.setJobTitle("SALESMAN");
    emp.setSalary(new Float("1500"));
    //
    // initialize a proxied bean, advised with a "Validator" Interceptor
    Employee scott = mySecondAOP.getEmployee();
    // note: scott is a proxy that implements the Employee interface. Internally
    // this proxy referes to an EmployeeImpl object that we never get to see directly
    scott.setFirstName("John");
    scott.setLastName("Smith");
    scott.setJobTitle("MANAGER");
    // we have advised scott with an advise that sets a default salary value for Managers:
    System.out.println("My dear employee " + scott.getFirstName() + " " + scott.getLastName() + " (" + scott.getJobTitle() + ") = " + scott.getSalary());
    Employee alsoscott = scott; // alsoscott holds a reference to the same object (a proxy) that scott references
   //
    // here we swap the EmployeeImpl underlying the scott reference with the emp EmployeeImpl
    mySecondAOP.swap(scott, emp);
    System.out.println("My dear employee " + scott.getFirstName() + " " + scott.getLastName() + " (" + scott.getJobTitle() + ") = " + scott.getSalary());
    // all references to the object scott used to reference are still valid. That mains: alsoscott refers to the same employee as scott
    System.out.println("Also  " + alsoscott.getFirstName() + " " + alsoscott.getLastName() + " (" + alsoscott.getJobTitle() + ") = " + alsoscott.getSalary());
    //
    // setting a property on emp now also means setting it on scott - as scott is a reference to a proxy for the same object that emp references
    emp.setSalary(new Float("2500"));
    System.out.println("My dear employee " + scott.getFirstName() + " " + scott.getLastName() + " (" + scott.getJobTitle() + ") = " + scott.getSalary());
    // scott was advised with a validator; although a swap has taken place and scott is now associated with another Employee object (or should we say
    // another EmployeeImpl object), this advise is still in place
    scott.setSalary(new Float("5000.32"));
    System.out.println("The new salary for " + scott.getFirstName()+ " " + scott.getLastName() + " (" + scott.getJobTitle() +  ") = " + scott.getSalary());
  }
}

The output from this little program is like this:

My dear employee John Smith (MANAGER) = 7500.0
My dear employee John Doe (SALESMAN) = 1500.0
Also  John Doe (SALESMAN) = 1500.0
My dear employee John Doe (SALESMAN) = 2500.0
Exception in thread main
java.lang.RuntimeException: Salary may not exceed 4000 for Salesmen such as John Doe
   	at nl.amis.spring.trial.EmployeeValidator.before(EmployeeValidator.java:16)
   	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
   	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
   	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:174)
   	at $Proxy0.setSalary(Unknown Source)
   	at nl.amis.spring.trial.HotSwappeableTargets.main(HotSwappeableTargets.java:83)
Process exited with exit code 1.

The second line in the output clearly illustrates that the application through its scott variable is now using the original emp backing object (John Doe) instead of the object earlier associated with the scott variable (John Smith). That is the reason why we see a violation of the business rule that Employees may not be called John Doe: we had implemented this logic through an advise on the object retrieved for scott using getEmployee(). The emp object was directly instantiated and not advised at all. Now this object has been brought under the advised scott proxy, it will be subject to this business logic aspect henceforth. We see in line 3 that the reference alsoscott is painlessly transferred to John Doe as well. Line 4 in the output indicates that updating the emp object also affects the results from scott.getSalary(). This is only logical since scott and emp refer in the end to the same EmployeeImpl object. Finally we see that the aspects advised on scott – for example the business rule that Salesmen should not earn more than 4000 – are still in place.

Why and when hot swappable targets?

Why would you want to change the object underlying the object references? Well, an example is – in fact, that is the inspiration for this whole article – the situation where your objects have originated from a read only query, from JDBC or perhaps an O/R framework like TopLink, and you want to start updating the object – with the intent of persisting the updates. If we were to proxy all these read-only objects when handed to the application, ensuring that they are advised with an Before interceptor that intercepts calls to the set methods we can arrange for these objects to automatically swap their underlying target; instead of using the read only object as a target, we could replace them with a (TopLink) working copy,a clone registered with the UnitOfWork. By becoming modified, the object is automatically added to the UnitOfWork and therefore eligle for persisting when the UnitOfWork commits. It seems that in conjunction with hot swappable targets, it might make sense to create a class that:

Resources

package nl.amis.spring.trial;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class EmployeeValidator implements MethodBeforeAdvice  {
  public EmployeeValidator() {
  }
  public void before(Method method, Object[] args, Object target) throws Throwable {
    Employee emp = (EmployeeImpl)target;
    if (method.getName().equalsIgnoreCase("setSalary")) {
      if ("SALESMAN".equalsIgnoreCase(emp.getJobTitle())) {
         // if the job of this employee is SALESMAN, he/she may not earn more than 4000
         float newSalary =  ((Float)args [ 0 ]).floatValue();
         if (newSalary > 4000) {
           throw new RuntimeException("Salary may not exceed 4000 for Salesmen such as "+emp.getFirstName()+" "+emp.getLastName());
         }
      }
    }
    if (method.getName().equalsIgnoreCase("setFirstName")) {
      if ("Doe".equalsIgnoreCase(emp.getLastName())) {
         // we do not want any employee to be called John Doe
         if ("John".equalsIgnoreCase((String)args [ 0 ])) {
           throw new RuntimeException("Employees should not be called John Doe. Choose another First Name please.");
         }
      }
    }
    if (method.getName().equalsIgnoreCase("setLastName")) {
      if ("John".equalsIgnoreCase(emp.getFirstName())) {
         // we do not want any employee to be called John Doe
         if ("Doe".equalsIgnoreCase((String)args [ 0 ])) {
           throw new RuntimeException("Employees should not be called John Doe. Choose another Last Name please.");
         }
      }
    }
    if (method.getName().equalsIgnoreCase("setJobTitle")) {
       if ("MANAGER".equalsIgnoreCase((String)args [ 0 ])) {
           // provide a decent starting value for Manager's Salaries
           if (null==emp.getSalary())
           { emp.setSalary(new Float(7500));
         }
      }
    }
  }
}
package nl.amis.hrm.domain;
import java.util.Date;
public interface Employee  {
  public String getFirstName();
  public void setFirstName(String FirstName);
  public String getLastName();
  public void setLastName(String LastName);
  public String getJobTitle();
  public void setJobTitle(String JobTitle);
  public Float getSalary();
  public void setSalary(Float Salary);
  public Date getHiredate();
  public void setHiredate(Date Hiredate);
}
package nl.amis.spring.trial;
import java.util.Date;
public class EmployeeImpl implements Employee {
  String FirstName;
  String LastName;
  String JobTitle;
  Float Salary;
  Date Hiredate;
  public EmployeeImpl() {
  }
  public String getFirstName() {
    return FirstName;
  }
  public void setFirstName(String FirstName) {
    this.FirstName = FirstName;
  }
  public String getLastName() {
    return LastName;
  }
  public void setLastName(String LastName) {
    this.LastName = LastName;
  }
  public String getJobTitle() {
    return JobTitle;
  }
  public void setJobTitle(String JobTitle) {
    this.JobTitle = JobTitle;
  }
  public Float getSalary() {
    return Salary;
  }
  public void setSalary(Float Salary) {
    this.Salary = Salary;
  }
  public Date getHiredate() {
    return Hiredate;
  }
  public void setHiredate(Date Hiredate) {
    this.Hiredate = Hiredate;
  }
}
  • implements the Collection interface
  • is configured with a Collection as input
  • is configured with an Advisor, an object that gets passed in an object and returned the advised proxy
  • implements most methods by calling them on the underlying Collection
  • implements the get(int index) method to not return the objects in the collection but instead proxied, advsied objects, using the Advisor object that it was set up with
  • Such an object can be instantiated with the results from TopLink queries and passed by the Service object as query-result. If the program starts changing the objects from this collection, they automatically become registered with the UnitOfWork. Steven (Davelaar) and myself discussed this and hope to find time during this year's ODTUG conference to get it working. Since I have only just started thinking about the Hot Swappability, I have not any other examples. But they will come to me, I am sure! . Download the JDeveloper 10.1.2 workspace with the code presented in this article: Note: You have to define the Spring Framework library in the project SpringAOP; this library should contain the spring jar-files that you can download below. You must also make sure that your library contains the log4.jar – since Spring relies on Log4J. Alternatively you can download Spring with all dependencies from the link below.To download the Spring Framework, go to All source code for this example is in the zip file. The main classes are here: and

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

Comments are closed.