Interface ControllerServiceProxyWrapper<T>

Type Parameters:
T - the type of the wrapped/proxied object

public interface ControllerServiceProxyWrapper<T>
The purpose of this interface is to help handle the following scenario: A Controller Service method returns a value which is wrapped in a proxy by the framework. Another method of the Controller Service receives the same proxy as an argument. (The Controller Service gets back the object.) This method expects the argument to be of the concrete type of the real return value. Since the proxy only preserves the interface of the real return value but not the concrete type, the method fails. To fix this, this interface is added to the proxy (along with the original interface) and the framework will get the real value via getWrapped() so the Controller Service will receive the real object. E.g.:
public interface IConnectionProviderService {
     IConnection getConnection();
     void closeConnection(IConnection);
 }

 public class ConnectionProviderServiceImpl {
     IConnection getConnection() {
         return new SimpleConnection();
     }

     void closeConnection(IConnection) {
         if (connection instanceof SimpleConnection) {
             ...
         } else {
             throw new InvalidArgumentException();
         }
     }
 }

 public class ConnectionUserProcessor {
     IConnectionProviderService service; #Set to ConnectionProviderServiceImpl

     void onTrigger() {
         IConnection connection = service.getConnection();

         # 'connection' at this point is a proxy of a 'SimpleConnection' object
         # So '(connection instanceof IConnection)' is true, but
         # '(connection instanceof SimpleConnection)' is false

         ...

         service.closeConnection(connection); # !! This would have thrown InvalidArgumentException
     }
 }
 
But why wrap the return value in a proxy in the first place? It is needed to handle the following scenario: A Controller Service method returns an object to a Processor. A method is called on the returned object int the Processor. This method tries to load a class that is in the same package as the return object. Since it tries to use the Processor classloader, it fails. E.g.:
package root.interface;

 public interface IReportService {
     IReport getReport();
 }
 public interface IReport {
     void submit();
 }


 package root.service;

 public class ReportServiceImpl {
     IReport getReport() {
         return new Report();
     }
 }
 public class ReportImpl {
     void submit() {
         Class.forName("roo.service.OtherClass");
         ...
     }
 }
 public class OtherClass {}


 package root.processor;

 public class ReportProcessor {
     IReportService service; #Set to ReportServiceImpl

     void onTrigger() {
         IReport report = service.getReport();
         ...
         report.submit(); # !! This would have thrown ClassNotFoundException
     }
 }
 
So in general there is a barrier between the Controller Service and the Processor (or another Controller Service) due to the fact that they have their own classloaders. When an object crosses the barrier, it needs to be proxied to be able to use its original classloader. Also when it crosses the barrier back again, it needs to be unproxied to preserve specific type information. Note that wrapping may not be necessary if the class is loaded by a classloader that is parent to both the Controller Service and the Processor, as in this case the 'barrier' not really there for instances of that class.
  • Method Summary

    Modifier and Type
    Method
    Description
     
  • Method Details

    • getWrapped

      T getWrapped()
      Returns:
      the object that is being wrapped/proxied.