Proxy Pattern
The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.
Proxy Pattern Diagram

Implementation
Creating Subject interface:
import java.rmi.*;
/* We extend Remote here to define methods below as remote interface */
public interface GumballMachineRemote extends Remote {
/* Serializable */
public int getCount() throws RemoteException; /* support RemoteException */
public String getLocation() throws RemoteException;
/* Unserializable */
public State getState() throws RemoteException;
}Serializing State:
import java.io.*;
/* Making State Serializable */
public interface State extends Serializable {
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}Reworking States:
public class NoQuarterState implements State {
/* Serialized class version identifier. Java uses it during deserialization
to check that the sender's and receiver's class definitions are compatible. */
private static final long serialVersionUID = 2L;
/* transient excludes this field from serialization.
After deserialization on the monitor side, this field is null. */
transient GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You inserted a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter");
}
public void turnCrank() {
System.out.println("You turned, but there's no quarter");
}
public void dispense() {
System.out.println("You need to pay first");
}
public String toString() {
return "waiting for quarter";
}
}Creating a Real Subject:
import java.rmi.*;
import java.rmi.server.*;
/* UnicastRemoteObject
This gives the ability to act as a remote service. */
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote {
private static final long serialVersionUID = 2L;
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState;
int count = 0;
String location;
/* As superclass throws RemoteException, this constructor also needs */
public GumballMachine(String location, int numberGumballs) throws RemoteException {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
this.location = location;
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
void setState(State state) {
this.state = state;
}
void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (count != 0) {
count = count - 1;
}
}
public void refill(int count) {
this.count = count;
state = noQuarterState;
}
public int getCount() {
return count;
}
public State getState() {
return state;
}
public String getLocation() {
return location;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public State getWinnerState() {
return winnerState;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2014");
result.append("\nInventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}Setting up RMI registry and running Real Subject:
import java.rmi.*;
public class GumballMachineTestDrive {
public static void main(String[] args) {
GumballMachineRemote gumballMachine = null;
int count;
if (args.length < 2) {
System.out.println("GumballMachine <name> <inventory>");
System.exit(1);
}
try {
count = Integer.parseInt(args[1]);
gumballMachine = new GumballMachine(args[0], count);
/* Publishing Stub */
Naming.rebind("//" + args[0] + "/gumballmachine", gumballMachine);
} catch (Exception e) {
e.printStackTrace();
}
}
}Run with
rmiregistryjava GumballMachineTestDrive austin.mightygumball.com 100
Creating a Proxy Client:
/* Import RMI package because we are using the RemoteException class below */
import java.rmi.*;
public class GumballMonitor {
GumballMachineRemote machine;
public GumballMonitor(GumballMachineRemote machine) {
this.machine = machine;
}
public void report() {
try {
System.out.println("Gumball Machine: " + machine.getLocation());
System.out.println("Current inventory: " + machine.getCount() + " gumballs");
System.out.println("Current state: " + machine.getState());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}Connecting to Proxies:
import java.rmi.*;
public class GumballMonitorTestDrive {
public static void main(String[] args) {
/* We create an array of locations, one for each machine */
String[] location = {"rmi://santafe.mightygumball.com/gumballmachine",
"rmi://boulder.mightygumball.com/gumballmachine",
"rmi://austin.mightygumball.com/gumballmachine"};
if (args.length >= 0) {
location = new String[1];
location[0] = "rmi://" + args[0] + "/gumballmachine";
}
/* We also create an array of monitors */
GumballMonitor[] monitor = new GumballMonitor[location.length];
for (int i=0;i < location.length; i++) {
try {
/* This returns a proxy to the remote machine.
Naming.lookup() is a static method in the RMI package that takes a location and
service name and looks it up in the rmiregistry at that location. */
GumballMachineRemote machine = (GumballMachineRemote) Naming.lookup(location[i]);
monitor[i] = new GumballMonitor(machine);
System.out.println(monitor[i]);
} catch (Exception e) {
e.printStackTrace();
}
}
/* Then we iterate through each machine and print out its report */
for (int i=0; i < monitor.length; i++) {
monitor[i].report();
}
}
}