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

Another benefit is that the client does not have to wait for the full processing by the server and it can continue with other tasks. The client can go about its business while it’s waiting for the server response. When results are available, they can be displayed immediately.

Depending on the implementation of the pattern, Callback can queue client requests, allowing the server to organize and prioritize its workload. It also potentially allows the server to notify clients of changes beyond the typical lifetime of a client. Web agents are a good example of this, since they allow a client to enter a query in one Web session, and be notified of the results in another.

One challenge of the Callback is that it requires a client to listen for the callback from the server. This often makes client code more complex, and increases the load on client systems. An additional drawback stems from the fact that Callback decouples the request from the client. This often makes it difficult to cancel or modify a request once it has been sent to the server.

Pattern Variants

Variations of the Callback pattern generally center on server processing strategies and approaches to client notification. Two major approaches are common in server-side processing:

Direct processing – With this approach, the server creates a worker thread to fulfill each client’s request. This is straightforward to implement, but is sometimes difficult to scale to large numbers of service requesters.

Request queue – The server maintains a queue of client requests and a pool of worker threads. The worker threads (see “ ” on page 231) are assigned to perform client processing on an ongoing basis.

A few options are available for client notification, depending on the application requirements:

Active callback – A client uses server-like process to listen for incoming communications. This allows the client to directly receive server notification.

Client polling – Also known as client pull , this requires a client to periodically check on the status of its request. When the request or parts of the request are complete, the client will request that information from the server.

Explicit acknowledgment – A server may retransmit a message until it receives client confirmation. This is sometimes used for

cases where the server processing can take longer than the client application’s lifetime. Although this is not relevant in TCP since the socket won’t open unless the client is there to do its part, it is meaningful when using unreliable communication technologies like UDP.

Related Patterns

Related patterns include Worker Thread (page 231). The Worker Thread pattern is used to help schedule client requests. The requests are put in a queue and the worker threads process them.

Example

Note:

For a full working example of this code example, with additional supporting classes and/or a RunPattern class, see “ Callback ” on page 525 of the “ Full Code Examples ” appendix.

In the Personal Information Manager, one of the items that can vary most in size is a project. A project might consist of only a few tasks, or it could be made up of hundreds or even thousands of individual work steps. This example demonstrates how the Callback pattern could be used to retrieve a project object stored on a server machine.

The interface CallbackServer defines a single server-side method, getProject. Note that the method requires callback information—the client machine name and the name of the RMI client object—in addition to the project ID. The class CallbackServerImpl implements this interface.

Example 4.15 CallbackServer.java

1.import java.rmi.Remote;

2.import java.rmi.RemoteException;

163

3.public interface CallbackServer extends Remote{

4.public void getProject(String projectID, String callbackMachine,

5.String callbackObjectName) throws RemoteException;

6.}

Example 4.16 CallbackServerImpl.java

1.import java.rmi.Naming;

2.import java.rmi.server.UnicastRemoteObject;

3.public class CallbackServerImpl implements CallbackServer{

4.private static final String CALLBACK_SERVER_SERVICE_NAME = "callbackServer";

5.public CallbackServerImpl(){

6.try {

7.

UnicastRemoteObject.exportObject(this);

8.

Naming.rebind(CALLBACK_SERVER_SERVICE_NAME, this);

9.}

10.catch (Exception exc){

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

12.}

13.}

14.

15.public void getProject(String projectID, String callbackMachine,

16.String callbackObjectName){

17.new CallbackServerWorkThread(projectID, callbackMachine, callbackObjectName);

18.}

19.

20.}

In the getProject method, CallbackServerImpl delegates the task of retrieving the project to a worker object, CallbackServerDelegate. This object runs on its own thread and does the work of retrieving a project and sending it to a client.

Example 4.17 CallbackServerDelegate.java

1.import java.net.MalformedURLException;

2.import java.rmi.Naming;

3.import java.rmi.NotBoundException;

4.import java.rmi.RemoteException;

5.public class CallbackServerDelegate implements Runnable{

6.private Thread processingThread;

7.private String projectID;

8.private String callbackMachine;

9.private String callbackObjectName;

10.

11.public CallbackServerDelegate(String newProjectID, String newCallbackMachine,

12.String newCallbackObjectName){

13.projectID = newProjectID;

14.callbackMachine = newCallbackMachine;

15.callbackObjectName = newCallbackObjectName;

16.processingThread = new Thread(this);

17.processingThread.start();

18.}

19.

20.public void run(){

21.Project result = getProject();

22.sendProjectToClient(result);

23.}

24.

25.private Project getProject(){

26.return new Project(projectID, "Test project");

27.}

28.

29.private void sendProjectToClient(Project project){

30.try{

31.

String url = "//" +

callbackMachine + "/" + callbackObjectName;

32.

Object remoteClient

= Naming.lookup(url);

33.

if (remoteClient instanceof CallbackClient){

34.

((CallbackClient)remoteClient).receiveProject(project);

35.

}

 

36.}

37.catch (RemoteException exc){}

38.catch (NotBoundException exc){}

39.catch (MalformedURLException exc){}

40.}

41.}

164

In the CallbackServerDelegate run method, the object retrieves a project by calling the getProject method, then sends it to a client with the sendProjectToClient method. The latter method represents the callback to the client; the CallbackServerDelegate makes a call to an RMI object of type CallbackClient on the client machine. The interface CallbackClient also defines a single RMI method, receiveProject.

Example 4.18 CallbackClient.java

1.import java.rmi.Remote;

2.import java.rmi.RemoteException;

3.public interface CallbackClient extends Remote{

4.public void receiveProject(Project project) throws RemoteException;

5.}

The implementer of CallbackClient, CallbackClientImpl, is both a client and a server. Its method requestProject looks up the CallbackServer and calls the remote method getProject. The class also defines the remote method receiveProject, which is called by the server work thread when the project is ready for the client. CallbackClientImpl has a boolean variable, projectAvailable, to allow a client program to determine when the project is ready for display.

Example 4.19 CallbackClientImpl.java

1.import java.net.InetAddress;

2.import java.net.MalformedURLException;

3.import java.net.UnknownHostException;

4.import java.rmi.Naming;

5.import java.rmi.server.UnicastRemoteObject;

6.import java.rmi.NotBoundException;

7.import java.rmi.RemoteException;

8.public class CallbackClientImpl implements CallbackClient{

9.private static final String CALLBACK_CLIENT_SERVICE_NAME = "callbackClient";

10.private static final String CALLBACK_SERVER_SERVICE_NAME = "callbackServer";

11.private static final String CALLBACK_SERVER_MACHINE_NAME = "localhost";

12.

13.private Project requestedProject;

14.private boolean projectAvailable;

16.public CallbackClientImpl(){

17.try {

18.

UnicastRemoteObject.exportObject(this);

19.

Naming.rebind(CALLBACK_CLIENT_SERVICE_NAME, this);

20.}

21.catch (Exception exc){

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

23.}

24.}

25.

26.public void receiveProject(Project project){

27.requestedProject = project;

28.projectAvailable = true;

29.}

30.

31.public void requestProject(String projectName){

32.try{

33.

String url = "//" + CALLBACK_SERVER_MACHINE_NAME + "/" +

34.

CALLBACK_SERVER_SERVICE_NAME;

Object remoteServer = Naming.lookup(url);

35.

if (remoteServer instanceof CallbackServer){

36.

((CallbackServer)remoteServer).getProject(projectName,

37.

InetAddress.getLocalHost().getHostName(),

38.

CALLBACK_CLIENT_SERVICE_NAME);

39.

}

40.

projectAvailable = false;

41.}

42.catch (RemoteException exc){}

43.catch (NotBoundException exc){}

44.catch (MalformedURLException exc){}

45.catch (UnknownHostException exc){}

46.}

47.

48.public Project getProject(){ return requestedProject; }

49.public boolean isProjectAvailable(){ return projectAvailable; }

50.}

The basic sequence of action is as follows. When a client requires a project, the CallbackClientImpl object

calls the method getProject on the CallbackServerImpl object. The CallbackServerImpl creates a

165

CallbackServerWorkThread object to retrieve the project. When the CallbackServerWorkThread completes its

task, it calls the client method receiveProject, sending the Project instance to the requester, the

CallbackClientImpl object.

166