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

clients. A server usually sends information about all its changes to clients, and the client has to be able to be contacted by the server, which might not be an easy task because of the client application or a firewall.

Pattern Variants

The most common variation in implementing a Successive Update strategy involves choosing an update approach that is a combination of both client pull and server push. Depending on an application’s requirements, an application can rely on client pull for routine events, but use server push for time-critical notifications. Applications that mix the two update strategies typically define time-critical notifications:

Notifications that could potentially cause an application error, such as deleting a customer record.

Normal events, such as those that cannot result in an error or loss of data, like creating a new customer.

For server push, the server module can transmit its notifications to explicitly defined clients, provided that the list of clients is relatively short and the information is not large. As the number of supported clients grows, the server can quickly become overwhelmed with such an approach, although not as quickly as when being polled by all the clients. For notification of many clients, broadcasting information is often preferable. In this case, the server sends its message to one or more servers, which retransmit to clients that have registered themselves as interested participants. This approach is used in the Web and multicasting technologies, and reduces the load on the originating server.

Related Patterns

Related patterns include the following:

Observer (page 94) – Clients often use the Observer pattern to register with the server for a server push solution.

Callback (page 238) – Successive update might use the Callback pattern for server push.

Mediator (page 77) – In server push solutions, the server often acts as a Mediator, sending updates to interested clients as the updates are received.

Example

Note:

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

The example code shows a simple client pull solution for the Personal Information Manager. Clients use the server to centralize information about tasks they are working on. Each client stays up-to-date by periodically requesting updates from the server.

In the sample code, the PullClient class retrieves a task for a client. Its responsibility is to locate the RMI server so that it can request tasks on a regular basis.

Example 4.20 PullClient.java

1.import java.net.MalformedURLException;

2.import java.rmi.Naming;

3.import java.rmi.NotBoundException;

4.import java.rmi.RemoteException;

5.import java.util.Date;

6.public class PullClient{

7.private static final String UPDATE_SERVER_SERVICE_NAME = "updateServer";

8.private static final String UPDATE_SERVER_MACHINE_NAME = "localhost";

9.private ClientPullServer updateServer;

10.private ClientPullRequester requester;

11.private Task updatedTask;

12.private String clientName;

13.

14.public PullClient(String newClientName){

15.clientName = newClientName;

16.try{

170

17. String url = "//" + UPDATE_SERVER_MACHINE_NAME + "/" + UPDATE_SERVER_SERVICE_NAME; 18. updateServer = (ClientPullServer)Naming.lookup(url);

19.}

20.catch (RemoteException exc){}

21.catch (NotBoundException exc){}

22.catch (MalformedURLException exc){}

23.catch (ClassCastException exc){}

24.}

25.

26.public void requestTask(String taskID){

27.requester = new ClientPullRequester(this, updateServer, taskID);

28.}

29.

30.public void updateTask(Task task){

31.requester.updateTask(task);

32.}

33.

34.public Task getUpdatedTask(){

35.return updatedTask;

36.}

37.

38.public void setUpdatedTask(Task task){

39.updatedTask = task;

40.System.out.println(clientName + ": received updated task: " + task);

41.}

42.

43.public String toString(){

44.return clientName;

45.}

46.}

When the client wants to receive updates on a task, it calls the method requestTask on the PullClient. The PullClient object creates a worker thread (see “ Worker Thread ” on page 231), which is the ClientPullRequester object. This object resides on the client, and regularly issues a request to the server for updated task information.

Example 4.21 ClientPullRequester.java

Y

1.

import java.rmi.RemoteException;

2.

 

 

 

public class ClientPullRequester implements Runnable{

3.

 

 

 

L

private static final int DEFAULT_POLLING INTERVAL = 10000;

4.

private Thread processingThread;

F

M

5.

private PullClient parent;

 

6.

private ClientPullServer updateServer;A

7.

private String taskID;

 

E

 

8.

private boolean shutdown;

 

 

9.

 

T

 

private Task currentTask = new TaskImpl();

10.

private int pollingInterval = DEFAULT_POLLING_INTERVAL;

11.

 

 

 

 

12.public ClientPullRequester(PullClient newParent, ClientPullServer newUpdateServer,

13.String newTaskID){

14.parent = newParent;

15.taskID = newTaskID;

16.updateServer = newUpdateServer;

17.processingThread = new Thread(this);

18.processingThread.start();

19.}

20.

21.public void run(){

22.while (!isShutdown()){

23.

try{

24.

currentTask = updateServer.getTask(taskID, currentTask. getLastEditDate());

25.

parent.setUpdatedTask(currentTask);

26.

}

27.

catch (RemoteException exc){ }

28.

catch (UpdateException exc){

29.

System.out.println(" " + parent + ": " + exc.getMessage());

30.

}

31.

try {

32.

Thread.sleep(pollingInterval);

33.

}

34.

catch (InterruptedException exc){ }

35.}

36.}

37.

38.public void updateTask(Task changedTask){TEAM FLY PRESENTS

39.try{

171

40. updateServer.updateTask(taskID, changedTask);

41.}

42.catch (RemoteException exc){ }

43.catch (UpdateException exc){

44. System.out.println(" " + parent + ": " + exc.getMessage());

45.}

46.}

48.public int getPollingInterval(){ return pollingInterval; }

49.public boolean isShutdown(){ return shutdown; }

50.

51.public void setPollingInterval(int newPollingInterval){ pollingInterval =

newPollingInterval; }

52.public void setShutdown(boolean isShutdown){ shutdown = isShutdown; }

53.}

The RMI server's behavior is defined by the ClientPullServer interface and managed by the ClientPullServerImpl class. Two methods allow clients to interact with a server, getTask and updateTask.

Example 4.22 ClientPullServer.java

1.import java.rmi.Remote;

2.import java.rmi.RemoteException;

3.import java.util.Date;

4.public interface ClientPullServer extends Remote{

5.public Task getTask(String taskID, Date lastUpdate) throws RemoteException, UpdateException;

6.public void updateTask(String taskID, Task updatedTask) throws RemoteException,

UpdateException;

7.}

Example 4.23 ClientPullServerImpl.java

1.import java.util.Date;

2.import java.rmi.Naming;

3.import java.rmi.server.UnicastRemoteObject;

4.public class ClientPullServerImpl implements ClientPullServer{

5.private static final String UPDATE_SERVER_SERVICE_NAME = "updateServer";

6.public ClientPullServerImpl(){

7.try {

8.

UnicastRemoteObject.exportObject(this);

9.

Naming.rebind(UPDATE_SERVER_SERVICE_NAME, this);

10.}

11.catch (Exception exc){

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

13.}

14.}

15.

16.public Task getTask(String taskID, Date lastUpdate) throws UpdateException{

17.return UpdateServerDelegate.getTask(taskID, lastUpdate);

18.}

19.

20.public void updateTask(String taskID, Task updatedTask) throws UpdateException{

21.UpdateServerDelegate.updateTask(taskID, updatedTask);

22.}

23.}

The class UpdateServerDelegate performs the server-side behavior for ClientPullServerImpl. Specifically, it retrieves Task objects, and ensures that up-to-date copies of Tasks are provided to clients by comparing the last update Date.

Example 4.24 UpdateServerDelegate.java

1.import java.util.Date;

2.import java.util.HashMap;

3.public class UpdateServerDelegate{

4.private static HashMap tasks = new HashMap();

6.public static Task getTask(String taskID, Date lastUpdate) throws UpdateException{

7.if (tasks.containsKey(taskID)){

8.

Task storedTask = (Task)tasks.get(taskID);

9.

if (storedTask.getLastEditDate().after(lastUpdate)){

10.

return storedTask;

11.

}

12.

else {

13.

throw new UpdateException("Task " + taskID + " does not need to be updated",

14.

UpdateException.TASK_UNCHANGED);

}

172

15.}

16.else{

17. return loadNewTask(taskID);

18.}

19.}

21.public static void updateTask(String taskID, Task task) throws UpdateException{

22.if (tasks.containsKey(taskID)){

23.if (task.getLastEditDate().equals(((Task)tasks.get(taskID)). getLastEditDate())){

24.

((TaskImpl)task).setLastEditDate(new Date());

25.

tasks.put(taskID, task);

26.

}

27.

else{

28.

throw new UpdateException("Task " + taskID + " data must be refreshed before

29.

editing", UpdateException.TASK_OUT_OF_DATE);

}

30.}

31.}

33.private static Task loadNewTask(String taskID){

34.Task newTask = new TaskImpl(taskID, "", new Date(), null);

35.tasks.put(taskID, newTask);

36.return newTask;

37.}

38.}

173