What Is a Proxy?

To put it simply, a proxy is an object that stands in for another object and appears to perform the first object's functions. The object that the proxy imitates is called the implementation object. Instead of using the implementation directly, classes that want to access the features of the implementation use a proxy. For example, in an EJB system, the implementations are across the network from the clients that want to use them. The advantage of proxies is that they allow you to insert code between the implementation and the proxy. A proxy can hide complex tasks such as network communication and transaction management from the proxy user, all without changing the implementation object's functionality. To better understand how proxies work, let's look at a class that implements two simple methods (see Example 10-1).

Example 10-1. An implementation object
package oracle.hcj.proxies;
public class SomeClassImpl {
 private String userName;
 public SomeClassImpl(final String userName) {
 this.userName = userName;
 }
 public void someMethod( ) {
 System.out.println(this.userName);
 }
 public void someOtherMethod(final String text) {
 System.out.println(text);
 }
}


Since you want a user to be able to use this class without having direct access to it, you need to create a proxy. Let's begin with a simple proxy; we'll add to it as we proceed. Implement a series of methods that are identical to the implementation (see Example 10-2).

Example 10-2. A simple proxy
package oracle.hcj.proxies;
public class SomeClassProxy {
 private final SomeClassImpl impl;
 public SomeClassProxy(final SomeClassImpl impl) {
 this.impl = impl;
 }
 public void someMethod( ) {
 this.impl.someMethod( );
 }
 public void someOtherMethod(String text) {
 this.impl.someOtherMethod(text);
 }
}


This code creates a proxy to SomeClassImpl that looks exactly like SomeClassImpl itself. However, instead of providing the functionality of the class, SomeClassProxy forwards requests to SomeClassProxy to the implementation (stored in the impl member variable). Now that you have your proxy, you can work with it as if you were working with the implementation itself. At this point, you may wonder why this special code isn't inserted into the implementation object itself. There are many reasons why you may not want to do this:

  • The source code of the implementation class may not be accessible. This could be the case if third-party libraries were implemented by your company or provided by a separate department.
  • The new functionality added is often not consistent with the functionality of the implementation object. Consider a class that models the functionality of a satellite. If you want to access this class remotely across a LAN or WAN, you need to write some networking code. However, placing the networking code inside the implementation violates the principle that an object should implement only one concept.
  • The implementation could expose features that should remain hidden from various users. For example, the billing department doesn't need to access the shipping portion of an order. You can use several proxies to the same implementation object to show an altered implementation specifically for the billing department.
  • You may want to hide the names of the implementation methods from the users of the implementation for security reasons. This would require a special view proxy in which the methods in the proxy have different names than those in the implementation.
  • The functionality may be dependent on the context in which the object is being used. For example, a computer connected directly to a robotic arm in a factory does not need networking code to access the arm, but the control center on the other side of the factory does need this code.
  • The functionality may be dependent on the development phase. For example, you can use a proxy to implement tracing behavior in a program that counts the number of times the object is called. This code may not be necessary in the deployed release of the software.
  • The location of the implementation object may be changeable, as it is in enterprise programming. The objects performing the actual operations in an enterprise network often shift locations depending on load-balancing and fault-tolerance code. You would need an intelligent proxy to locate the object to provide the services of that object to the user.

There are other reasons to create proxies as well. However, these are some of the most common. In fact, the entire Enterprise JavaBeans programming paradigm is modeled on the last reason.

Network Proxies and CORBA

Life was hard before proxies. If a user wanted to call a remote object, he would have to set up a TCP connection to the remote object and then create a protocol to communicate with it. Finally, he would use the protocol to talk to the remote object, request an action, and get a response. The invention of the network proxy model made this all much easier. The network proxy model is used throughout modern programming. It is the core functionality in CORBA, RMI, EJB, and Web Services programming. It makes all of these technologies easier by hiding the difficulties of managing the network communication protocols. The invention of common object request broker architecture (CORBA) by the Object Management Group (OMG) was a turning point in the coding of network software. The programmer didn't need to invent protocols. Also, the problem of marshalling (flattening data to streams across the network and then reconstructing this data on the other side) was solved. CORBA managed all of this using proxies.

Screenshot

CORBA and other Internet inter-orb protocol (IIOP) technologies have many other functionalities as well. However, these are far beyond the scope of this tutorial.


Distributed systems such as CORBA, RMI, and EJB use network proxies in which the programmer defines the interface to an object and then runs a pre-compile-time tool to generate code based on that interface. From one interface definition, the user ends up with four different classes. However, the user on the client side simply uses one of these objects as if it were the actual implementation. The proxy class handles the protocol negotiation, data marshalling, and networking. This allows the business app programmer to concentrate on code for his company rather than reinvent the wheel. Screenshot-1 shows a UML deployment diagram of how this works.

Screenshot-1. The distributed proxy model
Java figs/HCJ_1001.gif

In this diagram, the developer building the GUI component needs to concern himself only with the interfaces to the services on the server. The proxies convert the data to be sent over TCP/IP. The GUI developer doesn't need to worry about the fact that the billing system is implemented on the server side as a pool of objects that balance load. All he needs to know is that he has a box in which he can put something in and extract a result. Using proxies is an example of distributed programming, which is one of the most significant advances in software engineering since the invention of object-oriented engineering. It takes object-oriented engineering to a new level by allowing the objects to be distributed around a network of computers transparently. Although this distribution is possible without proxies, it would be much more difficult and costly. Following the example of CORBA, many other technologies that use the network proxy model sprung up. Among these technologies were EJB, JAX RPC, and other web services technologies. All of these would be unfeasible without the concept of proxies.

Factories

To get a proxy to an object, you usually have to use a factory object, which combines the proxy and the implementation into one unit. One reason why factories are used is because you don't particularly care which kind of object implements your functionality, as long as it implements it properly. With EJB, the factory object finds the implementation on the network using another object or a unique identifier. The factory then binds the proxy to the implementation and gives you back the proxy. At this point, you can work with the proxy as if it were the implementation of the object, even though the implementation is on a different computer. Factory objects are used to obtain proxies because they often do a great deal of work setting up proxies. They may need to allocate network resources to the proxy, or allow the proxy to perform initialization routines to prepare itself for use. Also, factory objects usually implement a security policy that prevents unauthorized or dangerous activity. Let's create a factory for your proxy from Example 10-2. In Example 10-3, the code that creates the implementation object simply passes in the name "Fred" as the username:

SomeClassProxy proxy = new SomeClassProxy(new SomeClassImpl("Fred"));


However, you want to pass the actual username of the user in the constructor to SomeClassImpl. If you allow the user of the proxy to pass in anything he wants, you cannot be sure that he will pass in his real username. To make sure you get the real username, use a proxy factory (see Example 10-3).

Example 10-3. A basic proxy factory
package oracle.hcj.proxies;
public class SomeClassFactory {
 public final static SomeClassProxy getProxy( ) {
 SomeClassImpl impl = new SomeClassImpl(System.getProperty("user.name"));
 return new SomeClassProxy(impl);
 }
}


In this code, the proxy factory sets up the object and binds to the implementation. In a real system, the user would simply not be able to create the actual implementation objects. For example, in an EJB system, the user simply doesn't have the actual class files for the implementation. Instead, he must ask the EJB server for a proxy to that object. Using the proxy factory is quite easy:

package oracle.hcj.proxies;
public class DemoProxyFactory {
 public static final void main(final String[] args) {
 SomeClassProxy proxy = SomeClassFactory.getProxy( );
 proxy.someMethod( );
 proxy.someOtherMethod("Our Proxy works!");
 }
}


Here, the proxy factory is used to create a proxy for the desired implementation class. Since this is the only way the user would be able to access the implementation in a real system, he can never submit a false username. Whenever you access any EJB object, you take advantage of a factory called a home interface. Since a home interface is second nature to EJB programmers, many don't realize that this is actually a factory, nor do they realize that the object they are using on the client side is actually a quite sophisticated proxy.

Client and Server Objects

With proxies and distributed programming, the terms client and server take on a different meaning than what many client/server programmers are used to. In standard client/server programming, a client is the user interface and the server implements the business logic of the system. In this context, the clients never have any business logic at all. In proxy-based programming, the client is simply an object that takes advantage of the services of another object, called the server (object). Therefore, a single object may be both a client to another object and a server to various clients, all at the same time. For example, consider an object that provides billing in an online store. The Billing object is a client to the CreditCardVerification object since it uses this object to verify the credit card before billing begins. However, the Billing object is also a server to the Ordering object because the Ordering object requires the services of the Billing object so it can order products for the customer. In this case, the user interface is most likely a client (and not a server) to various server objects. However, it's not uncommon to see a mix of client and server objects at all levels of a distributed app. Understanding the dichotomy between client and server is important when talking about proxies because the terminology often conflicts with terminology from the old method of client/server programming.

      
Comments