Sitemap

Remote Proxy pattern

8 min readFeb 28, 2023

--

The Proxy pattern is exactly what it sounds like: it is a proxy, to something. We will explore this “something”, and other details here. The second function of the pattern is that it also controls and manages access to that “something”.

This second part, of providing access, has multiple ways, and this gives rise to the following variations of the Proxy pattern:

  1. The Remote Proxy controls access to a remote object
  2. The Virtual Proxy controls access to an object that is expensive to create
  3. The Protection Proxy controls access to an object based on access rights, thereby protecting it.

In this article however, we limit our scope to the Remote Proxy. For implementation, we use Java’s Remote Method Invocation(RMI) package. The RMI package enables an object residing in one JVM(or any runtime in general) to invoke objects residing in another runtime. This is the essence of the Remote Proxy pattern.

Real life analogy

We will take the example a Remote Office vs Local Office for sales. A Remote Office is one which is located in a far away place. Let’s assume it provides the services of showing the catalogs, providing quotes, selling the items, and providing post sales service. But that does not mean you have to visit the remote store. How about a local office, that allows you to have access to the mentioned services?

Note that this local office does not really do the work, rather it takes your request, for instance, of selling something to you. The request is then forwarded to the actual remote office, where the actual action of selling happens. The local office then receives the sold item from the remote office, and hands it over to you.

It then appears as if the local object then just adds an extra hop. Is this even beneficiary? Let’s see.

To a client, the local office does look like the remote office, but it does not do much apart from taking your requests and forwarding it to the remote office; and handing you back the responses(items) that come from the remote office. But where its usefulness comes into play is that it spares you as the client from any paperwork or other formalities. It handles all of those on its own. You only need to be concerned about your requirements. For all you know, the local office is THE office. It fulfills all your requirements. So the Local Office acts as a proxy for the Remote Office. It controls and manages your access, and handles the internal complexities. This is what our scenario looks like now- with a local office and a remote sales office, each having the own runtimes(locations). We will explore the keywords- “proxy”, “remote” and “internal complexities" turn by turn.

Press enter or click to view image in full size

The interface( the Proxy part)

Note the first part of the Proxy pattern- of being a proxy. Intuitively, for an entity to act as a proxy for an object, it has to act like the object. What does this translate to in programming? It means the entity has to have the same interface as the object it is proxying for.

Let’s check out the remote and local offices here. We will first define the interface, having the above mentioned methods:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface SalesOfficeRemote extends Remote {
void showCatalog() throws RemoteException;
double provideQuote() throws RemoteException;
void sell() throws RemoteException;
void providePostSalesService() throws RemoteException;
}

The Remote interface of Java’s RMI that the SalesOfficeRemote interface extends makes it a remote service and exposes its methods, such that non-local objects can invoke them. Also note that all of the methods throw RemoteExceptions. This is necessary to gracefully handle errors that occur in the network calls.

This is the interface upon which our remote office- the SalesOffice builds upon, implementing the SalesOfficeRemote interface’s methods.

Implementation

public class SalesOffice implements SalesOfficeRemote {

double quote=100000;

@Override
public void showCatalog() {
System.out.println("showCatalog() called in remote");
}

@Override
public double provideQuote(){
System.out.println("provideQuote() called in remote");
return this.quote;
}

@Override
public void sell() {
System.out.println("sell() called in remote");
}

@Override
public void providePostSalesService() {
System.out.println("providePostSalesService() called in remote");
}
}

So far, so good on the remote side. Let’s go the the local office.

Local

public class LocalOffice {
SalesOfficeRemote salesOfficeRemote;

public LocalOffice(SalesOfficeRemote salesOfficeRemote){
this.salesOfficeRemote=salesOfficeRemote;
}

void showCatalogLocal() throws RemoteException{
System.out.println("Calling showCatalog() from local");
this.salesOfficeRemote.showCatalog();
}

double provideQuoteLocal() throws RemoteException{
double quote=this.salesOfficeRemote.provideQuote();
System.out.println("Calling provideQuote() from local");
return quote;
}

void sellLocal() throws RemoteException {
System.out.println("Calling sell() from local");
this.salesOfficeRemote.sell();
}

void providePostSalesServiceLocal() throws RemoteException{
System.out.println("Calling providePostSalesService() from local");
this.salesOfficeRemote.providePostSalesService();
}
}

Let’s check out the LocalOffice class above. How does it connect with the remote SalesOffice? It HAS-A reference to the SalesOfficeRemote. Note that this is the interface, NOT its concrete implementation SalesOffice. For each of its corresponding methods, it calls the the corresponding method exposed by the interface SalesOfficeRemote, using the SalesOfficeRemote reference it has .

What we did till now is have SalesOffice implement SalesOfficeRemote, and have LocalOffice contain an instance of the interface SalesOfficeRemote, and have corresponding methods that invoke the actual methods on this interface.

RMI Registry(the Remote part)

But how does that spare the client from any paperwork or other formalities? How is the client saved from the complexities of network calls, or marshalling and unmarshalling the requests and responses?

Get Snigdha Sanat’s stories in your inbox

Join Medium for free to get updates from this writer.

Compared to the simplified diagram we had earlier for the client and server runtimes, we actually have this:

Press enter or click to view image in full size

We will go over each blocks in the diagram, and understand them.

First let’s understand the RMI registry. The RMI registry acts like a phone book. When an object(SalesOfficeRemote in our case) wants to act as a remote object, it has to register itself in the RMI registry. The client then looks up for the same remote object in this registry, so that it can access it.

Registering the remote object in RMI

public class RemoteProgram extends SalesOffice {
public RemoteProgram(){ }

public static void main(String[] args) {

try{
SalesOffice salesOffice=new SalesOffice();

//Note that we add the impl object here, and cast it to type of
//the interface
SalesOfficeRemote salesOfficeRemote=
(SalesOfficeRemote)UnicastRemoteObject.exportObject(salesOffice,0);

/*binds salesOfficeRemote to mySalesOffice. "mySalesOffice" is the
name that will be used to look up in the rmi registry*/
Registry registry = LocateRegistry.getRegistry();
registry.bind("mySalesOffice", salesOfficeRemote);
}
catch (Exception e) {
System.err.println("RemoteProgram Server exception:"+e.toString());
e.printStackTrace();
}

}//main
}

Let’s go line by line of the try block. We first create an instance of the concrete class SalesOffice. In the next line, we export this object using UnicastRemoteObject, such that it is available to receive incoming calls(from the local clients/local office of course), in the port mentioned. We have passed ‘0’(2nd argument of exportObject) as the port, which is a wildcard port. You can choose any other port. Notice the return type here. It is cast to SalesOfficeRemote which is the interface, and NOT the concrete implementation SalesOffice.

Next, we get the reference to the rmi registry using getRegistry(), on which then we bind(register) the salesOfficeRemote object of type SalesOfficeRemote interface. The name under which it is registers, in this case “mySalesOffice”, is important. This is what will be used by the local office to look up. That is it for the registering part.

Looking up the RMI

The client then has to look up for this object in the registry. Here it goes:

public class LocalClient {
public LocalClient(){}

public static void main(String[] args) {
try{

Registry registry = LocateRegistry.getRegistry(null);
//Note that the return type is of type interface SalesOfficeRemote
SalesOfficeRemote salesOfficeRemoteProxy =
(SalesOfficeRemote) registry.lookup("mySalesOffice");

LocalOffice localOffice=new LocalOffice(salesOfficeRemoteProxy);

localOffice.showCatalogLocal();
localOffice.provideQuoteLocal();
localOffice.sellLocal();
localOffice.providePostSalesServiceLocal();
}
catch(Exception e){
System.err.println("Client exception:"+e.toString());
e.printStackTrace();
}

}

}

Let’s check out the try block. We get the reference of the rmi registry using getRegistry(). The host argument ‘null’ means the host is the localhost. This is because the rmi registry would be running in the localhost. It then looks up for the proxy, using the name “mySalesOffice” used in registering above. And that is it. Rest is just about instantiating the LocalOffice object and calling its methods.

The proxy object( Handling internal complexities)

Notice a nuance here, a rather important one. The type of the registry.lookup() method is SalesOfficeRemote. But what is this object actually? This object is the client stub(proxy), which is of type SalesOfficeRemote. This is internally and dynamically generated. And it will always be of type of the interface, in this case SalesOfficeRemote. This enables it to become a reference to the remote object SalesOffice, as both of them implement the same interface SalesOfficeRemote, and hence has the same methods. When we bind/register the service object(salesOfficeRemote), RMI swaps it for the proxy and puts it in the registry, which is what the LocalClient gets through the lookup() method.

The obtained SalesOfficeRemote PROXY object is then passed to the LocalOffice class to instantiate it.The LocalOffice instance is then used to call its methods. This is how all of them come together:

Press enter or click to view image in full size

The Client Stub(Proxy) accepts the requests from the LocalOffice, and the marshals(serializes) it for transferring it over the wire(network). It is this Proxy that the LocalOffice actually talks to, which in turns talks to the remote environment. LocalOffice calls local methods on this Proxy, which resides in the local runtime of the LocalOffice. The proxy handles all the low level network call details. But the Proxy in reality does not have the business logic. That is possessed by the SalesOffice(the remote object). But to the client, it poses as THE remote object. Hence the name proxy.

Server skeleton( Handling internal complexities)

The client Proxy contacts the Server Skeleton. The Server Skeleton receives the request from the proxy and then deserializes it. It then invokes the real methods on the remote object (SalesOffice), which contains the actual business logic. It then serializes and sends the response received from it over the network back to the Proxy. The Proxy then deserializes and gives it back to the client(LocalOffice). So even to the remote object SalesOffice, it appears as if the call has originated from its local runtime. The Server Skeleton makes it appear so. The generation of Server Skeleton also happens dynamically.

So both the Proxy and Server Skeleton make things look local to their respective LocalOffice and remote SalesOffice, and handles networking the complexities on their behalf.

Handling exceptions

But as the calls are over the wire, the client has to accommodate exceptions. Hence the RemoteExceptions on all the methods of the interface SalesOfficeRemote. Note that Java does not mandate the implementations of a method, that throws exceptions in its interface declarations, to have exceptions. That is why the method implementations in the implementation SalesOffice have been skipped.

Even the binding and lookup procedures in RemoteProgram and LocalClient respectively are put in try-catch block. When communicating over the network, things often go wrong.

Definition

The Proxy pattern provides a placeholder(Proxy) for a remote object(SalesOffice) to control access to it(by handling network details).

Code examples

All the code example are here.

--

--