Dynamic Proxies In Java and .NET

Tom examines how proxies separate cross-cutting concerns, then explores—in both Java and C#—a new twist with the traditional Proxy pattern that promotes reuse and decreases complexity.


July 01, 2003
URL:http://www.drdobbs.com/windows/dynamic-proxies-in-java-and-net/184405378

Jul03: Dynamic Proxies in Java and .NET

Tom is a senior developer for Hubbard One. He can be contacted at [email protected].


Proxies versus Decorators


Discrete modules such as components and classes are designed to maximize functional cohesion while minimizing the interaction between modules. In typical object-oriented systems, these components are defined by the functional boundaries of the domain, even though many concerns are not aligned along these functional boundaries. Concerns such as logging, security, persistence, threading, and caching usually touch most areas of an application. While it is common to pull these concerns into separate subsystems, thereby limiting the degree to which they are spread throughout the code, explicit calls to these subsystems decrease cohesion and increase coupling of resulting modules. Consequently, one of the primary goals of software design is separation of concerns.

Attempts at separating cross-cutting concerns are not new. Microsoft's MTS/COM+ and Java EJB services have separated cross-cutting concerns for years. For instance, you write COM+ and EJB components without worrying about security or threading, since the underlying COM+ server and EJB container frameworks address these issues. In this article, I examine how proxies separate cross-cutting concerns, then explore a new twist on the traditional Proxy pattern that promotes reuse and decreases complexity. The complete source code (in C# and Java) that implements the techniques presented is available electronically (see "Resource Center," page 5).

Interestingly, aspect-oriented programming (AOP) deals with this exact issue. AOP defines a framework for implementing concerns that cross cut many different modules ("aspects") in a loosely coupled manner. But aspects represent a framework that is unlike traditional languages, which lack formal support for AOP. More recently, there are add-ons for Java such as AspectJ (see "Aspect-Oriented Programming with AspectJ," by William Grosso, DDJ, August 2002). Still, AOP is not currently an option for many projects.

Proxy Pattern

AOP aside, the cleanest way of developing cross-cutting concerns is via interception. Instead of creating actual business objects, you can create proxies to these objects that implement the same interface as the real business objects, but with added functionality (also see the accompanying text box entitled "Proxies versus Decorators"). To illustrate how to apply proxies to separate cross-cutting concerns, consider an Employee component in either .NET or Java. Figure 1 shows the Employee interface, an Abstract Factory (see Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma et al., Addison-Wesley, 1995) for returning instances of an Employee and simple implementations of both the interface and the factory.

Suppose you want to add logging to the Employee class to trace application flow through an Employee object. The obvious way would be to modify the Employee implementation class and add a call to a logging component in all of the method calls. While this works, it decreases the cohesion of the Employee implementation class, while increasing coupling. The class now needs to know about the logging framework, making it impossible to use the Employee implementation class in projects that don't have the same logging framework without modifying the source code. Additionally, the process can be error prone in practice since it is easy to forget to modify a method.

Alternatively, consider the additions in Figure 2 that employ the Proxy pattern to separate logging concerns. The original Employee implementation class remains untouched, but I've added the LoggingEmployee and LoggingEmployeeFactory classes. The only change to start using this new logging functionality is to instantiate LoggingEmployeeFactory in place of the original SimpleEmployeeFactory. LoggingEmployeeFactory returns instances of LoggingEmployee that look like a typical Employee object (as they implement the Employee interface), but are just wrappers around a real Employee object. LoggingEmployee logs method requests, then forwards the request to the actual Employee object. You now have a logging Employee component without reducing cohesion or increasing coupling of the underlying classes.

This solution is cleaner than the original, but not without problems. These problems become more apparent by recognizing that applications typically consist of many components with cross-cutting concerns. For each new concern, a new concrete class for each domain class needs to be developed, and factory methods for these classes must be created or modified. In systems with many domain classes and cross-cutting concerns, the number of classes makes this scheme too complex to be viable. Code generation reduces some complexity (by automatically generating classes for each concern) but ideally, there is one class for each cross-cutting concern that could be applied to all classes in the domain, instead of a class for each concern/domain class combination.

Dynamic Proxies

Fortunately, both Java and .NET support dynamic proxies. According to the Java documentation (http://java.sun.com/j2se/1.3/docs/index.html):

A dynamic proxy class is a class that implements a list of interfaces specified at run time such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface.

In other words, a dynamic proxy is an instance of a class created at run time that can serve as a proxy for an interface or set of interfaces. Classes generated at run time may not be exciting to Smalltalk aficionados, but this behavior has traditionally been unavailable in C-based, object-oriented languages.

Java Dynamic Proxies

In Java, dynamic proxies are created using the classes java.lang.reflect.Proxy and java.lang.reflect.InvocationHandler. The Proxy class has a factory method newProxyInstance(), which returns dynamic proxy objects given a class loader, an array of interfaces to implement, and an instance of java.lang.reflect.InvocationHandler. The interface InvocationHandler, which you must implement for dynamic proxies, consists of a single method, invoke(), which is called whenever a call to the proxy occurs. The invoke() method is passed a reference to the dynamic proxy, the method that was called, and an array of the arguments that were passed to the method.

To see this in action, I refactor the logging Employee component to use dynamic proxies. Instead of creating an Employee-specific logging proxy, I create the class LoggingProxy as a generic logging proxy that wraps any object. As expected, LoggingProxy implements InvocationHandler with an implementation of invoke() that logs the method call, then uses reflection to invoke the method on the actual wrapped object. LoggingProxy also has the factory method getProxy(), which returns a dynamic proxy instance given an object to proxy. Because the syntax for creating dynamic proxies is often messy, it is common to hide the details for obtaining them in factory methods.

.NET Dynamic Proxies

There are several ways in .NET to intercept calls to objects at run time, thereby creating dynamic proxies. However, the easiest way is to enlist the use of the System.Runtime.Remoting.Proxies.RealProxy class. (The other common method is to have domain objects implement System.ContextBoundObject and have custom ContextAttributes perform the interception.) As evident by its namespace, RealProxy is used in the .NET Framework for remote operations, but actually performs a role similar to InvocationHandlers in Java. The RealProxy class has one abstract method, Invoke(), which takes an IMessage object as a parameter and returns an IMessage object as a result. The IMessage parameter object wraps the call information into one object, which can be unpacked to get the method being called and its arguments. The RealProxy method GetTransparentProxy() is a factory method that returns the actual proxy object, much like the newProxyInstance() factory in Java. One difference between .NET and Java remote proxies is that a RealProxy can only proxy one interface (or alternatively a MarshalByRef object), whereas a Java Proxy can proxy any number of interfaces.

Refactoring the C# version of the logging proxy, I again create a generic logging proxy that wraps any object. LoggingProxy extends RealProxy, implementing the abstract method Invoke(). The Invoke() method is slightly messier than the Java version, as the IMessage parameter needs to be cast into an IMethodCallMessage, which I then pick apart to get the method and parameters. The return value of the method also needs to be wrapped in an IMessage object, in this case a ReturnMessage, and returned from the proxy. Although .NET provides a way to return output parameters from a dynamic proxy, this example assumes that there are no output parameters. Also, the factory method for the .NET version takes in an object to proxy and the Type of the interface for which to proxy (alternatively, the Type of a MarshalByRef object).

Problems with Dynamic Proxies

Aside from some minor frustration with less than obvious syntax for creating dynamic proxies, the drawback of using dynamic proxies is speed of execution. In both Java and .NET, calls through dynamic proxies require the use of reflection, resulting in method calls being 5 to 200 times slower (at least in my informal testing). Of course, this relative decrease is in comparison to blazingly fast local method calls; in the case of method calls that perform significant business logic, both relative and absolute performance degradation may be imperceptible. As with all performance testing, your results may be different. In most cases, the slight performance degradation is an acceptable tradeoff for producing maintainable, reusable code.

Persistence Example

One place where performance of dynamic proxies likely will not affect overall system performance is with database calls. For example, consider a modified Employee component; this time, only in C#. I've added a manager/manages bidirectional relationship to the Employee class, and my new goal is to map Employees to a relational database (Figure 3).

For ease of illustration, I limit the mapping to the relatively simple case of loading instances from the database by object identifier (OID). But even with this simplification, an interesting problem remains: When an employee is retrieved from the database, ideally, I would like to load employees who are related to it; otherwise consumers of my Employee component will have to explicitly load them. If I load the related employees, however, I also want to load employees related to these related employees (for the same reason) and the employees related to these employees, and so forth. If there's no mechanism in place to stop this loading chain, the entire object graph of employees can be loaded into memory just to get one employee back from the database.

To avoid this, most object-relational mapping schemes employ some type of "lazy loading" pattern. As objects are loaded from the database, related objects are not all materialized at the same time. Instead, proxies that contain only partially loaded objects take their place. Only if the client navigates to one of these proxies does an "object fault" occur and result in the proxy loading the entire object. This can be an effective pattern when objects have many relationships that are not often traversed, as related objects never end up being loaded if they are not used.

Often, the proxies for related objects are the actual domain objects themselves, except that the objects just contain an OID. The getters and setters for all of the properties of the domain object first check if the object is just a "ghost" and, if so, load the actual object before returning a value. For example, you could modify the Employee class to have properties that look something like:

public string FirstName {

get {

if (!this.IsLoaded)

Load();

return name;

}

}

This approach works, but also is an example of a cross-cutting concern (the need for lazy loading) interfering with the domain logic. The Employee class shouldn't need to know anything about ghosts or lazy loading—it should stick to implementing the business logic of being an Employee.

I could, of course, hardcode an Employee ghost proxy, but as more domain components are added, I would need to go through the tedious task of creating a hardcoded proxy for each component. Fortunately, I can enlist the support of dynamic proxies to take care of this problem.

To keep the Employee class free of any database logic, I created a simple EmployeeMapper class, which makes Employee objects materialize by OID. EmployeeMapper has two public methods, Find() and FindByManager(), which return an Employee by OID and a list of Employees by manager OID, respectively. EmployeeMapper also keeps a Hashtable of loaded objects to avoid loading the same object twice.

The interesting logic lives in the Load() method. After checking to make sure the OID is not already loaded, the FirstName and LastName properties are set as expected. At this point, the Manager object and Manages list could be loaded as well by calling Find() on the manager foreign key and FindByManager() on the OID, but if I did this now, I would run into the exact problem described earlier.

To avoid this situation, dynamic proxies are created with Delegates to these functions and the corresponding OIDs (a Delegate in .NET is similar to a function pointer). The RealProxy implementation classes LazyLoadObjectProxy and LazyLoadListProxy have factory methods that take in a Delegate and an OID and return dynamic proxies. LazyLoadObjectProxy serves as the factory for dynamic proxies to domain objects, and LazyLoadListProxy serves as the factory for dynamic proxies to lists of domain objects. The Invoke() method of these classes checks if the wrapped object (IList in the case of LazyLoadListProxy) is null; if so, it calls the Delegate function with the OID before invoking the method.

The nice thing about this technique is its flexibility. Many object-relational mapping problems boil down to looking up objects by their primary key or by a foreign key, and the two Delegate/Proxy combinations handle these two cases perfectly. Dynamic proxies have afforded us a flexible, reusable solution for a common problem.

Conclusion

Applications that employ dynamic proxies to separate concerns are cleaner, more reusable, and contain less code than their traditional counterparts. While aspect-oriented programming may be the future solution for separating cross-cutting concerns, dynamic proxies offer a unique solution that you can implement today. In cases where performance drawbacks are not critical, the simplicity and reusability of dynamic proxies is a powerful solution to the classic problem of cross-cutting concerns.

DDJ

Jul03: Dynamic Proxies in Java and .NET

Figure 1: The Employee interface

Jul03: Dynamic Proxies in Java and .NET

Figure 2: Using the Proxy pattern to separate logging concerns.

Jul03: Dynamic Proxies in Java and .NET

Figure 3: Mapping Employees to a relational database.

Jul03: Dynamic Proxies in Java and .NET

Figure 4: General structure of both patterns.

Back to Article

Jul03: Proxies versus Decorators

Proxies versus Decorators

Proxy and Decorator design patterns are often confused and mislabeled, mostly because of the similarity in their implementation. While they have similar implementation, they are different in intent: Proxies provide some sort of access control to the object they proxy; while decorators add responsibilities to the object that they decorate.

Both patterns follow the general structure of Figure 4, with the proxy or decorator implementing the same interface or abstract class as the real subject. The prototypical example of a proxy is the remote proxy, where the client to a service cannot have a direct reference to the service itself (as it, say, resides on another computer); so instead, it has a reference to a proxy that can marshal requests and responses to and from the actual service. The prototypical examples of decorators are GUI widgets, where decorators add additional functionality to base components (such as adding a scrollable decorator to a panel component).

—T.B.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.