Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Applied Java™ Patterns - Stephen Stelting, Olav Maassen.pdf
Скачиваний:
202
Добавлен:
24.05.2014
Размер:
2.84 Mб
Скачать

Pattern Variants

Pattern variants include the following:

Both halves keep a reference to each other and send messages back and forth. In the classic form only the HOPP on the client side has a reference to the other half. This carries the consequence that communication can only be initiated by the client side HOPP by calling a method on the remote half. The remote half is able to respond only once to each call through the value it returns. When it's necessary that both halves can initiate communication, they both need a reference to the other half.

Smart HOPP (SHOPP). In this implementation, the local part of the HOPP can choose its counterpart from several connection strategies. This is helpful when, for instance, the application is distributed across a flexible network where machines come and go.

Asymmetric HOPP. In this version of the HOPP, pattern both halves don’t need to implement exactly the same interface. The remote part of the HOPP may provide a new proxy for the other half to use. That new proxy can contain new optimizations, or may even be a proxy to a different remote object.

Related Patterns

Related patterns include the following:

Mediator (page 77) – The objects that need to be mediated are distributed on multiple address spaces. The Mediator can use the HOPP to simplify communication.

Proxy, specifically Remote Proxy (page 197) – The HOPP pattern uses the Proxy pattern for transparent communication between the two halves.

Example

Note:

For a full working example of this code example, with additional supporting classes and/or a RunPattern class,

see “ Half-Object Plus Protocol (HOPP) ” on page 483 of the “

Y

Full Code Examples ” appendix.

 

 

L

 

 

F

A Personal Information Manager should be available Meverywhere, but its data should only be stored in one place.

This example uses RMI and the HOPP pattern toAhold a personal calendar on a server, while making its

 

T

 

information available to remote callers.

E

 

The Calendar interface defines all methods that will be available remotely. This interface extends

java.rmi.Remote and all its methods throw java.rmi.RemoteException. In this case, Calendar defines three methods: getHost, getAppointments, and addAppointment.

Example 3.29 Calendar.java

1.import java.rmi.Remote;

2.import java.rmi.RemoteException;

3.import java.util.Date;

4.import java.util.ArrayList;

5.public interface Calendar extends Remote{

6.public String getHost() throws RemoteException;

7.public ArrayList getAppointments(Date date) throws RemoteException;

8.public void addAppointment(Appointment appointment, Date date) throws RemoteException;

9.}

Calendar is implemented by two classes—the RMI remote object and its stub, or proxy. (See “ Proxy ” on page 197.) The remote object class, CalendarImpl, provides method implementations, while the stub manages communication to the remote object. The Java RMI compiler (rmic) needs to be run on the CalendarImpl to generate a stub and a skeleton class. The skeleton class is provided for backward compatibility, but, as of Java 1.2, is no longer necessary.

Example 3.30 CalendarImpl.java

1.import java.rmi.Naming;

2.import java.rmi.server.

3.

import

java.io.File;

TEAM FLY PRESENTS

4.

import

java.util.Date;

 

131

5.import java.util.ArrayList;

6.import java.util.HashMap;

7.public class CalendarImpl implements Calendar{

8.private static final String REMOTE_SERVICE = "calendarimpl";

9.private static final String DEFAULT_FILE_NAME = "calendar.ser";

10.private HashMap appointmentCalendar = new HashMap();

11.

12.public CalendarImpl(){

13.this(DEFAULT_FILE_NAME);

14.}

15.public CalendarImpl(String filename){

16.File inputFile = new File(filename);

17.appointmentCalendar = (HashMap)FileLoader.loadData(inputFile);

18.if (appointmentCalendar == null){

19. appointmentCalendar = new HashMap();

20.}

21.try {

22.

UnicastRemoteObject.exportObject(this);

23.

Naming.rebind(REMOTE_SERVICE, this);

24.}

25.catch (Exception exc){

26.System.err.println("Error using RMI to register the CalendarImpl " + exc);

27.}

28.}

29.

30.public String getHost(){ return ""; }

31.public ArrayList getAppointments(Date date){

32.ArrayList returnValue = null;

33.Long appointmentKey = new Long(date.getTime());

34.if (appointmentCalendar.containsKey(appointmentKey)){

35. returnValue = (ArrayList)appointmentCalendar.get(appointmentKey);

36.}

37.return returnValue;

38.}

39.

40.public void addAppointment(Appointment appointment, Date date){

41.Long appointmentKey = new Long(date.getTime());

42.if (appointmentCalendar.containsKey(appointmentKey)){

43. ArrayList appointments = (ArrayList)appointmentCalendar.get(appointmentKey);

44. appointments.add(appointment);

45.}

46.else {

47. ArrayList appointments = new ArrayList();

48. appointments.add(appointment);

49. appointmentCalendar.put(appointmentKey, appointments);

50.}

51.}

52.}

The CalendarImpl object must use the RMI support class UnicastRemoteObject so that it can handle incoming communication requests. In this case, the CalendarImpl constructor exports itself using the static method

UnicastRemoteObject.exportObject.

CalendarImpl also needs to have some way of publishing itself to the outside world. In RMI, the naming service is called the rmiregistry. It must be running before the CalendarImpl object is created. The rmiregistry is like a telephone book, providing a connection between a name and an object. When the CalendarImpl object registers itself with the rmiregistry through the rebind method it binds the name “calendarimp” to the stub of this remote object.

For a client to use the remote object it has to do a lookup in the rmiregistry of the host machine and receive the stub to the remote object. You can compare the stub to a telephone number. You can use that number from anywhere, on any phone, and you get connected to someone answering the number you’re calling. In this example, the CalendarHOPP class acts as the client for the CalendarImpl object.

Example 3.31 CalendarHOPP.java

1.import java.rmi.Naming;

2.import java.rmi.RemoteException;

3.import java.util.Date;

4.import java.util.ArrayList;

5.public class CalendarHOPP implements Calendar, java.io.Serializable {

6.private static final String PROTOCOL = "rmi://";

7.private static final String REMOTE_SERVICE = "/calendarimpl";

8.private static final String HOPP_SERVICE = "calendar";

132

9.private static final String DEFAULT_HOST = "localhost";

10.private Calendar calendar;

11.private String host;

12.

13.public CalendarHOPP(){

14.this(DEFAULT_HOST);

15.}

16.public CalendarHOPP(String host){

17.try {

18. this.host = host;

19. String url = PROTOCOL + host + REMOTE_SERVICE; 20. calendar = (Calendar)Naming.lookup(url);

21. Naming.rebind(HOPP_SERVICE, this);

22.}

23.catch (Exception exc){

24. System.err.println("Error using RMI to look up the CalendarImpl or register the CalendarHOPP " + exc);

25.}

26.}

28.public String getHost(){ return host; }

29.public ArrayList getAppointments(Date date) throws RemoteException{ return

calendar.getAppointments(date); }

30.

31.public void addAppointment(Appointment appointment, Date date) throws RemoteException

{calendar.addAppointment(appointment, date); }

32.}

The CalendarHOPP provides a key benefit over a conventional RMI client – it can locally run what would normally be remote methods. This can provide a substantial benefit in terms of communication overhead. The HOPP implements the same remote interface, but it will not export itself. It keeps a reference to the stub and forwards all the method calls to the stub that it does not (or cannot) handle. Now it can implement the methods that it wants to execute locally—in this example, the getHost method. The HOPP can be registered with the rmiregistry like a normal stub, but it now has the ability to execute methods locally.

133