source: tmcsimulator/trunk/src/tmcsim/cadsimulator/CADClientConnector.java @ 664

Revision 664, 17.5 KB checked in by jdalbey, 4 years ago (diff)

Multifile commit - revise source to match revisions to config filenames. Fix broken system tests. Fix defect #160.

Line 
1package tmcsim.cadsimulator;
2
3import java.io.IOException;
4import java.io.ObjectInputStream;
5import java.io.ObjectOutputStream;
6import java.net.Socket;
7import java.net.SocketException;
8import java.util.Observable;
9import java.util.Observer;
10import java.util.logging.Level;
11import java.util.logging.Logger;
12
13import javax.xml.parsers.DocumentBuilderFactory;
14
15import org.w3c.dom.Document;
16import org.w3c.dom.Element;
17import org.w3c.dom.Node;
18
19import tmcsim.common.CADProtocol;
20import tmcsim.common.ObserverMessage;
21import tmcsim.common.CADEnums.CAD_KEYS;
22import tmcsim.common.CADProtocol.CAD_CLIENT_CMD;
23import tmcsim.common.CADProtocol.CAD_COMMANDS;
24import tmcsim.common.CADProtocol.CAD_SIMULATOR_CMD;
25
26
27/**
28 * CADClientConnector handles communication between the CAD Simulator and
29 * remote CAD Clients.  Each instance of this class communicates with
30 * a CAD Client through a socket.  The run() method continuously checks to see
31 * if data has been received from the client.  If there is data, it is parsed,
32 * and the resulting action is performed by the CADScreenManager.  See the
33 * receiveObject() method description for more information.  The
34 * CADSimulatorClient is set up as an Observer of the CADScreenManager to listen
35 * for ObserverMessage objects.  For each object received, the appropriate
36 * action is taken, resulting in data being transmitted to the CAD Client.
37 * See the update() method description for more information.  The
38 * CADScreenManager is set up as an observer of the Coordinator to listen
39 * for simulation data updates.
40 *
41 * @author Matthew Cechini (mcechini@calpoly.edu)
42 * @version $Date: 2006/06/14 00:12:38 $ $Revision: 1.5 $
43 */
44public class CADClientConnector extends Thread implements Observer {
45   
46    /** Error Logger. */
47    private static Logger cadLogger = Logger.getLogger("tmcsim.cadsimulator");
48   
49    /** CADScreenManager object containing the data for managing the CAD Client's view information. */
50    private CADScreenManager screenManager;
51   
52    /** Socket used for communication with the CAD Client. */
53    private Socket theSocket;
54   
55    /** ObjectOutputStream for writing objects to the socket. */
56    private ObjectOutputStream out;
57   
58    /** ObjectInputStream for reading objects from the socket. */
59    private ObjectInputStream in;
60   
61    /**
62     * Constructor.  A CADScreenManager is instantiated to manage the output
63     * transmitted to the remote CAD Client.  This object is set up as an
64     * observer to that manager to listen for data that will be transmitted
65     * across the socket.  The CADScreenManager is set up as an observer
66     * of the Coordinator.  At construction, streams are created to handle
67     * reading and writing to the Socket.  When complete, the sendScreenRefresh()
68     * method is called to initialize the client.
69     *
70     *
71     * @param newSocket The socket to use for data transmission
72     * @throws IOException if there is an error in getting the output or input streams
73     * from the socket.
74     */
75    CADClientConnector(Socket newSocket) throws IOException{
76       
77        screenManager = new CADScreenManager(CADServer.theCoordinator);     
78        CADServer.theCoordinator.addObserver(screenManager);
79        screenManager.addObserver(this);
80       
81       
82        theSocket = newSocket;       
83        out       = new ObjectOutputStream(theSocket.getOutputStream());
84        in        = new ObjectInputStream(theSocket.getInputStream());   
85   
86        //initialize the CAD client
87        sendScreenRefresh();
88    }
89   
90    /**
91     * Method declaration for the Thread.run() method.  While the thread is not
92     * interrupted, read Objects from the socket and call the receiveObject()
93     * method to parse the data.  If there is an IOException in communicating
94     * with the client, interrupt this thread and close the streams and Socket.
95     */
96    public void run() {
97       
98        try { 
99       
100            while(!isInterrupted()) {
101                receiveObject(in.readObject());         
102            }
103        } 
104        catch (ClassCastException cce) {
105            cce.printStackTrace();
106        }
107        catch (ClassNotFoundException cnfe) {
108            cnfe.printStackTrace();
109        }
110        catch (java.io.EOFException except)
111        {
112            cadLogger.logp(Level.INFO, "CADClientConnector", "run", "Client appears to have shutdown, removing client.\n");
113            disconnectClient();
114        }
115        catch (SocketException except)
116        {
117            if (except.getMessage().endsWith("reset"))
118            {
119                cadLogger.logp(Level.INFO, "CADClientConnector", "run", "Connection reset, removing client.\n");
120                disconnectClient();
121            }
122        }
123        catch (IOException ioe) {                   
124            cadLogger.logp(Level.SEVERE, "CADSimulatorClient", "run", 
125                    "Error in reading from Client socket: " + 
126                    theSocket.getInetAddress() + ", proceeding.\n", ioe);
127           
128            //disconnectClient();
129        }
130    }
131   
132    /**
133     * This method is called to disconnect from the remote CAD Client.
134     * This object's thread is interrupted and the streams and socket
135     * are closed.  This object is removed as an observer of the
136     * CADScreenManager and the CADScreenManager is removed as an
137     * observer of the Coordinator.  The viewer is then notified
138     * of a disconnecting client.
139     */
140    protected void disconnectClient() {
141        this.interrupt();
142       
143        try { out.close(); } catch (Exception e) {}
144        try { in.close(); } catch (Exception e) {}
145        try { theSocket.close(); } catch (Exception e) {}
146
147        screenManager.deleteObserver(this);
148        CADServer.theCoordinator.removeObserver(screenManager);
149        //CADSimulator.theViewer.disconnectClient();
150    }
151   
152   
153
154    /**
155     * Observer method.  The update argument is cast to an ObserverMessage
156     * object and the message type is used to define the action taken by the
157     * CADSimulatorClient.  Command messages are created in the form of XML
158     * Document. The root Element name is value from the CAD_SIMULATOR_CMD
159     * enumeration.  The root text content is the value Object from the
160     * received Observer argument.
161     *
162     * The following table describes the messages sent for the ObserverMessage
163     * types.<br>
164     *
165     *<table cellpadding="2" cellspacing="2" border="1"
166    * style="text-align: left; width: 250px;">
167    *  <tbody>
168    *    <tr>
169    *      <th>Observer Message Type<br></th>
170    *      <th>Command Message Type<br></th>
171    *      <th>Data Content<br></th>
172    *    </tr>
173    *    <tr>
174    *      <td>SCREEN_UPDATE<br></td>
175    *      <td>UPDATE_STATUS</td>
176    *      <td>Screen update map String.</td>
177    *    </tr>
178    *    <tr>
179    *      <td>TIME_UPDATE<br></td>
180    *      <td>UPDATE_TIME</td>
181    *      <td>CAD time String.  (HHMM)</td>
182    *    </tr>
183    *    <tr>
184    *      <td>ROUTED_MESSAGE<br></td>
185    *      <td>UPDATE_MSG_COUNT</td>
186    *      <td>Number of messages.</td>
187    *    </tr>
188    *    <tr>
189    *      <td><br></td>
190    *      <td>UPDATE_MSG_UNREAD</td>
191    *      <td>Boolean flag to designate unread messages.</td>
192    *    </tr>
193    *    <tr>
194    *      <td>CAD_INFO_MESSAGE<br></td>
195    *      <td>CAD_INFO</td>
196    *      <td></td>
197    *    </tr>
198    *    <tr>
199    *      <td>REFRESH_VIEW<br></td>
200    *      <td>UPDATE_SCREEN</td>
201    *      <td>Current CAD model XML data.</td>
202    *    </tr>   
203    *  </tbody>
204    *</table>
205     *
206     * @see ObserverMessage
207     * @see CAD_SIMULATOR_CMD
208     */ 
209    public void update(Observable o, Object arg) {
210       
211        ObserverMessage oMessage = (ObserverMessage)arg;
212        CAD_SIMULATOR_CMD simCmd = null;
213       
214        switch(oMessage.type) {
215            case SCREEN_UPDATE:
216                simCmd = CAD_SIMULATOR_CMD.UPDATE_STATUS;
217                break;
218                       
219            case TIME_UPDATE:
220                simCmd = CAD_SIMULATOR_CMD.UPDATE_TIME;
221                break;
222               
223            case ROUTED_MESSAGE:
224                sendRoutedMessageUpdate();
225                break;
226               
227            case CAD_INFO_MESSAGE:
228                simCmd = CAD_SIMULATOR_CMD.CAD_INFO;
229                break;     
230               
231            case REFRESH_VIEW:
232                sendScreenRefresh();
233                break;
234        }
235       
236
237        if(simCmd != null) {
238            try {
239               
240                Document cmdDoc = DocumentBuilderFactory.newInstance()
241                        .newDocumentBuilder().newDocument();
242                cmdDoc.appendChild(cmdDoc.createElement(
243                        simCmd.type));
244                cmdDoc.getDocumentElement().appendChild(
245                        cmdDoc.createTextNode(oMessage.value.toString()));
246                transmitCommand(cmdDoc);
247   
248            } catch (Exception e) {
249                cadLogger.logp(Level.SEVERE, "CADSimulatorClient", "update", 
250                        "Error in transmitting a command to client.", e);
251            }           
252        }
253       
254    }
255   
256    /**
257     * This method acts as a helper method to create a XML Document
258     * update messages with the number of routed messages and
259     * whether there are unread messages for this client.  These
260     * two messages are sent separately due to the defined
261     * command protocol.
262     */
263    private void sendRoutedMessageUpdate() {
264
265        try {           
266            Document cmdDoc = DocumentBuilderFactory.newInstance()
267                .newDocumentBuilder().newDocument();
268            Element docElem = cmdDoc.createElement(
269                    CAD_SIMULATOR_CMD.UPDATE_MSG_COUNT.type);
270            docElem.appendChild(cmdDoc
271                    .createTextNode(String.valueOf(screenManager
272                            .getCurrentCADModel().numberRoutedMessages)));
273            cmdDoc.appendChild(docElem);
274            transmitCommand(cmdDoc);
275           
276
277            cmdDoc = DocumentBuilderFactory.newInstance()
278                .newDocumentBuilder().newDocument();
279            docElem = cmdDoc.createElement(
280                    CAD_SIMULATOR_CMD.UPDATE_MSG_UNREAD.type);
281            docElem.appendChild(cmdDoc
282                    .createTextNode(String.valueOf(screenManager
283                            .getCurrentCADModel().unreadMessages)));
284            cmdDoc.appendChild(docElem);
285            transmitCommand(cmdDoc);           
286
287        } catch (Exception e) {
288            cadLogger.logp(Level.SEVERE, "CADSimulatorClient", 
289                    "sendRoutedMessageUpdate", 
290                    "Error in transmitting a command to client.", e);
291        }   
292    }
293   
294    /**
295     * This method acts as a helper method to create an XML Document
296     * update message with the current CAD Model's XML information.
297     */
298    private void sendScreenRefresh() {
299       
300        try {           
301            Document cmdDoc = DocumentBuilderFactory.newInstance()
302                    .newDocumentBuilder().newDocument();
303            Element docElem = cmdDoc.createElement(
304                    CAD_SIMULATOR_CMD.UPDATE_SCREEN.type);
305           
306            screenManager.getCurrentCADModel().toXML(docElem);
307           
308            cmdDoc.appendChild(docElem);
309            transmitCommand(cmdDoc);
310
311        } catch (Exception e) {
312            cadLogger.logp(Level.SEVERE, "CADSimulatorClient", 
313                    "sendScreenRefresh", 
314                    "Error in transmitting a command to client.", e);
315        }               
316    }
317   
318    /**
319     * This method parses the data that has been received on the socket.  The
320     * Data is cast to an XML Document and the root element determines the
321     * data content.  The possible root elements and the corresponding action
322     * are explained below. <br>
323     * <code>
324     * -----------<br>
325     * TERMINAL_REGISTER<br>
326     *
327     * The CAD position and user ID are parsed from the Element and these
328     * values are sent to the CADScreenManager for use.<br>
329     * -----------<br>
330     * SAVE_COMMAND_LINE<br>
331     *
332     * The current command line text is parsed from the Element and sent
333     * to the CADScreenManager for user.
334     * <br>
335     * -----------<br>
336     * TERMINAL_CMD_LINE<br>
337     * The CAD command is parsed from the Element and converted to a
338     * CAD_CLIENT_CMD enumeration that is used to call the correct
339     * method in the CADScreenManager to perform the command.
340     * <br>
341     * -----------<br>
342     * TERMINAL_FUNCTION<br>
343     *
344     * The key value is parsed from the Element and converted to a CAD_KEYS
345     * enumeration that is sent to the CADScreenManager to perform the
346     * associated action.
347     * <br>
348     * -----------<br>
349     * TERMINATE<br>
350     *
351     * <br>
352     * -----------<br>
353     * </code>
354     * @param receivedData String of data received on the socket.
355     *
356     * @see CADProtocol
357     */
358    private void receiveObject(Object rxData) throws IOException {
359       
360        try {   
361           
362            Element root  = ((Document)rxData).getDocumentElement();
363       
364            switch(CAD_CLIENT_CMD.fromString(root.getNodeName())) {
365           
366                case TERMINAL_REGISTER:
367                    Node positionNode = root.getChildNodes().item(0);
368                    screenManager.setCADPosition(Integer.parseInt(positionNode.getTextContent()));
369                   
370                    Node userIDNode   = root.getChildNodes().item(1);
371                    screenManager.setCADUserID(userIDNode.getTextContent());                           
372                    break;         
373                 
374                case SAVE_COMMAND_LINE:
375                    screenManager.receiveCommandLine(root.getTextContent());
376                    break;
377                case TERMINAL_CMD_LINE:
378                   
379                    Node commandNode = root.getChildNodes().item(0);
380
381                    switch(CAD_COMMANDS.fromFullName(commandNode.getNodeName())) {
382                        case INCIDENT_BOARD:
383                            screenManager.incidentBoardRequest((Element)commandNode);
384                            break;
385                        case INCIDENT_UPDATE:       
386                            screenManager.incidentUpdateRequest((Element)commandNode);
387                            break;
388                        case INCIDENT_INQUIRY:                 
389                            screenManager.incidentInquiryRequest((Element)commandNode);
390                            break;
391                        case INCIDENT_SUMMARY:             
392                            screenManager.incidentSummaryRequest((Element)commandNode);
393                            break;
394                        case ROUTED_MESSAGE:           
395                            screenManager.routedMessageRequest((Element)commandNode);
396                            break;
397                        case ENTER_INCIDENT:           
398                            screenManager.enterIncidentRequest((Element)commandNode);
399                            break;
400                        case TERMINAL_OFF:             
401                            screenManager.terminalOffRequest();
402                            break;
403                        case APP_CLOSE:             
404                           
405                            try {
406                                Document cmdDoc = DocumentBuilderFactory.newInstance()
407                                        .newDocumentBuilder().newDocument();
408                                cmdDoc.appendChild(cmdDoc.createElement(CAD_SIMULATOR_CMD.
409                                        APP_CLOSE.type));                               transmitCommand(cmdDoc);
410                   
411                            } catch (Exception e) {
412                                cadLogger.logp(Level.SEVERE, "CADSimulatorClient", "update", 
413                                        "Error in transmitting a command to client.", e);
414                            }   
415
416                            //disconnectClient();
417                            break;
418                        case UNKNOWN:
419                            //TODO
420                            break;
421                    }
422                    break;
423           
424                case TERMINAL_FUNCTION:             
425                    screenManager.receiveCommand(CAD_KEYS.fromValue(
426                            root.getTextContent().substring(
427                                    0, root.getTextContent().indexOf(":")),                         
428                            new Integer(root.getTextContent().substring(
429                                    root.getTextContent().indexOf(":") + 1))));
430                    break;
431            }     
432        }
433        catch (ClassCastException cce) {
434            cadLogger.logp(Level.SEVERE, "CADSimulatorClient", 
435                    "receiveObject", 
436                    "Incorrect object received from client.", cce);
437        }
438    }   
439   
440    /**
441     * This method transmits the Document command message to the remote
442     * CAD Client.  If an exception occurs in writing to the socket, an
443     * Exception is thrown and socket communication is closed.
444     *
445     * @param data The data being transmitted
446     * @throws IOException if there is an exception in writing to the socket.
447     */
448    private void transmitCommand(Document data) throws IOException {
449 
450        try {
451            out.writeObject(data);
452            out.flush();
453        } catch (IOException ioe) {
454            cadLogger.logp(Level.SEVERE, "CADSimulatorClient", 
455                    "transmitCommand",  "Error writing to Client socket: " + 
456                    theSocket.getInetAddress() + ", continuing.\n", ioe);
457
458            //disconnectClient();
459        }
460           
461    }
462} 
Note: See TracBrowser for help on using the repository browser.