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

Router

The Router can be useful at various places in the example application. In almost every situation where there is more than one interested party in any event, you can use the Router. The Router is essentially an implementation of a listener structure; you will see some similarities.

The code for the Message class is shown here. It is a container for the source (an InputChannel) and the actual message—in this case, some String.

Example A.245 Message.java

1.import java.io.Serializable;

2.public class Message implements Serializable{

3.private InputChannel source;

4.private String message;

5.

6.public Message(InputChannel source, String message){

7.this.source = source;

8.this.message = message;

9.}

10.

11.public InputChannel getSource(){ return source; }

12.public String getMessage(){ return message; }

13.}

Example A.246 InputChannel.java

1.import java.io.Serializable;

2.public interface InputChannel extends Serializable{}

The OutputChannel is the interface that defines the method for sending the message to the target. Since the OutputChannel can be used to communicate between machines, it is defined as a remote interface.

Example A.247 OutputChannel.java

1.import java.rmi.Remote;

2.import java.rmi.RemoteException;

3.public interface OutputChannel extends Remote{

4.public void sendMessage(Message message) throws RemoteException;

5.}

The Router uses a hash map to store links between the specific InputChannel and various OutputChannels. When it receives a message, it looks up the destinations in its map.

It loops through the collection and sends the message to each of the destinations. In this example, the Router creates a worker thread (see “ Worker Thread ” on page 517) to send a message to each of its OutputChannel objects.

Thread pools are often used to improve performance in applications such as these.

Example A.248 Router.java

1.import java.rmi.Naming;

2.import java.rmi.RemoteException;

3.import java.rmi.server.UnicastRemoteObject;

4.import java.util.HashMap;

5.public class Router implements OutputChannel{

6.private static final String ROUTER_SERVICE_NAME = "router";

7.private HashMap links = new HashMap();

8.

9.public Router(){

10.try {

11.

UnicastRemoteObject.exportObject(this);

12.

Naming.rebind(ROUTER_SERVICE_NAME, this);

13.}

14.catch (Exception exc){

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

16.}

17.}

19.public synchronized void sendMessage(Message message) {

20.Object key = message.getSource();

21.OutputChannel[] destinations = (OutputChannel[])links.get(key);

354

22.new RouterWorkThread(message, destinations);

23.}

24.

25.public void addRoute(InputChannel source, OutputChannel[] destinations) {

26.links.put(source, destinations);

27.}

28.

29.private class RouterWorkThread implements Runnable{

30.private OutputChannel [] destinations;

31.private Message message;

32.private Thread runner;

33.

34.private RouterWorkThread(Message newMessage, OutputChannel[] newDestinations){

35.

message = newMessage;

36.

destinations = newDestinations;

37.

runner = new Thread(this);

38.

runner.start();

39.

}

40.

 

41.public void run() {

42.

for (int i = 0; i < destinations.length; i++){

43.

try{

44.

destinations[i].sendMessage(message);

45.

}

46.

catch(RemoteException exc){

47.

System.err.println("Unable to send message to " + destinations[i]);

48.

}

49.

}

50.}

51.}

52.}

When using the Router pattern, be careful about the size of message to be delivered. Generally, the message should be as small as possible. It is easy to be fooled by some Java objects, though. An object might have references to other objects, which refer to other objects, and so on—and what seemed like a small object might turn out to be very large indeed. For instance, sending a java.awt.Button is not a good idea, because the whole GUI will be serialized and sent.

It's a lot like buying your child a toy in a store. The purchase of a single Out-law Robot Laser Geek might not seem expensive at first, but by the time you get all the accessories (extra laser pistol, laser-spitting horn-rimmed glasses), you might wonder if it would just be cheaper to buy him or her a sweater.

In this example, the InputKey class implements the InputChannel interface. It must be sent to the Router using RMI, so this class must redefine the hashCode and equals methods to make sure objects on different JVMs can be tested for equality.

Example A.249 InputKey.java

1.public class InputKey implements InputChannel{

2.private static int nextValue = 1;

3.private int hashVal = nextValue++;

4.public int hashCode(){ return hashVal; }

5.public boolean equals(Object object){

6.if (!(object instanceof InputKey)){ return false; }

7.if (object.hashCode() != hashCode()){ return false; }

8.return true;

9.}

10.}

The RouterClient class provides a client to the Router; this class both sends and receives messages using RMI. The method sendMessageToRouter transmits a message to the central router, and the method sendMessage (defined by the OutputChannel interface) receives messages from the Router.

Example A.250 RouterClient.java

1.import java.rmi.Naming;

2.import java.rmi.server.UnicastRemoteObject;

3.import java.rmi.RemoteException;

4.public class RouterClient implements OutputChannel{

5.private static final String ROUTER_CLIENT_SERVICE_PREFIX = "routerClient";

6.private static final String ROUTER_SERVER_MACHINE_NAME = "localhost";

7.private static final String ROUTER_SERVER_SERVICE_NAME = "router";

8.private static int clientIndex = 1;

9.private String routerClientServiceName = ROUTER_CLIENT_SERVICE_PREFIX + clientIndex++;

355

10.private OutputChannel router;

11.private Receiver receiver;

13.public RouterClient(Receiver newReceiver){

14.receiver = newReceiver;

15.try {

16.

UnicastRemoteObject.exportObject(this);

17.

Naming.rebind(routerClientServiceName, this);

18.

String

url = "//" + ROUTER_SERVER_MACHINE_NAME + "/" + ROUTER_SERVER_SERVICE_NAME;

19.

router

= (OutputChannel)Naming.lookup(url);

20.}

21.catch (Exception exc){

22.

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

23.

}

24.

}

25.

26.

 

27.public void sendMessageToRouter(Message message){

28.try{

29. router.sendMessage(message);

30.}

31.catch (RemoteException exc){}

32.}

33.

34.public void sendMessage(Message message){

35.receiver.receiveMessage(message);

36.}

37.

38.public String toString(){

39.return routerClientServiceName;

40.}

41.}

Each RouterClient communicates with a client represented by the RouterGui class. This class provides a simple Swing GUI for sending and receiving messages via the Router. RouterGui implements the Receiver interface, which allows the RouterClient to provide it with real-time updates when it receives a Router message.

Example A.251 Receiver.java

1.public interface Receiver{

2.public void receiveMessage(Message message);

3.}

Example A.252 RouterGui.java

1.import java.awt.Container;

2.import java.awt.event.ActionListener;

3.import java.awt.event.ActionEvent;

4.import java.awt.event.WindowAdapter;

5.import java.awt.event.WindowEvent;

6.import javax.swing.JFrame;

7.import javax.swing.BoxLayout;

8.import javax.swing.JButton;

9.import javax.swing.JTextArea;

10.import javax.swing.JScrollPane;

11.import javax.swing.JTextField;

12.import javax.swing.JLabel;

13.import javax.swing.JPanel;

14.import java.io.Serializable;

15.public class RouterGui implements ActionListener, Receiver{

16.private static int instanceCount = 1;

17.private RouterClient routerClient;

18.private JFrame mainFrame;

19.private JButton exit, clearDisplay, sendMessage;

20.private JTextArea display;

21.private JTextField inputTextField;

22.private InputChannel inputChannel;

23.

24.public OutputChannel getOutputChannel(){

25.return routerClient;

26.}

27.

28.public RouterGui(InputChannel newInputChannel){

29.inputChannel = newInputChannel;

30.routerClient = new RouterClient(this);

31.}

356

32.

33.public void createGui(){

34.mainFrame = new JFrame("Demonstration for the Router pattern - GUI #" +

instanceCount++);

35.Container content = mainFrame.getContentPane();

36.content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));

38.JPanel displayPanel = new JPanel();

39.display = new JTextArea(10, 40);

40.JScrollPane displayArea = new JScrollPane(display);

41.display.setEditable(false);

42.displayPanel.add(displayArea);

43.content.add(displayPanel);

44.

45.JPanel dataPanel = new JPanel();

46.dataPanel.add(new JLabel("Message:"));

47.inputTextField = new JTextField(30);

48.dataPanel.add(inputTextField);

49.content.add(dataPanel);

50.

51.JPanel controlPanel = new JPanel();

52.sendMessage = new JButton("Send Message");

53.clearDisplay = new JButton("Clear");

54.exit = new JButton("Exit");

55.controlPanel.add(sendMessage);

56.controlPanel.add(clearDisplay);

57.controlPanel.add(exit);

58.content.add(controlPanel);

59.

60.sendMessage.addActionListener(this);

61.clearDisplay.addActionListener(this);

62.exit.addActionListener(this);

63.inputTextField.addActionListener(this);

65.mainFrame.addWindowListener(new WindowCloseManager());

66.mainFrame.pack();

67.mainFrame.setVisible(true);

68.}

69.

70.public void actionPerformed(ActionEvent evt){

71.Object source = evt.getSource();

72.if (source == sendMessage){ sendMessage(); }

73.else if (source == inputTextField){ sendMessage(); }

74.else if (source == clearDisplay){ clearDisplay(); }

75.else if (source == exit){ exitApplication(); }

76.}

77.

78.private class WindowCloseManager extends WindowAdapter{

79.public void windowClosing(WindowEvent evt){

80. exitApplication();

81.}

82.}

84.private void exitApplication(){

85.System.exit(0);

86.}

87.

88.private void clearDisplay(){

89.inputTextField.setText("");

90.display.setText("");

91.}

92.

93.private void sendMessage(){

94.String data = inputTextField.getText();

95.routerClient.sendMessageToRouter(new Message(inputChannel, data));

96.inputTextField.setText("");

97.}

98.

99.public void receiveMessage(Message message){

100.display.append(message.getMessage() + "\n");

101.}

102.}

RunPattern coordinates a demonstration of the pattern by creating a series of RouterGui objects. In the example, each RouterGui is connected up to some of the others through the Router. This means that a message sent by RouterGui # 4 will be delivered to all of the GUIs, while one sent from RouterGui # 1 will be sent to GUIs # 2 and 3.

357