package tmcsim.client; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.Properties; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JWindow; import javax.swing.UIManager; import tmcsim.client.cadclientgui.CADClientGUI; import tmcsim.client.cadclientgui.CardfileReader; import tmcsim.client.cadclientgui.GUIScriptReader; import tmcsim.client.cadclientgui.data.CADData; import tmcsim.client.cadclientgui.screens.Login; import tmcsim.client.cadclientgui.screens.ScreenManager; import tmcsim.common.CADEnums; import tmcsim.common.SimulationException; import tmcsim.interfaces.CADClientInterface; import tmcsim.interfaces.CoordinatorInterface; /** * CADClient is the main class for the CAD Client application. The main method * instantiates an instance of the CADClient object with the default properties * file "..\config\CADClient.properties" or the first argument fom the command * line invocation. Properties data values are used to bind socket communication * between the CAD Client and the CAD Simulator. The CADClientModel object is * instantiated and the CAD Client registers itself with the CAD Simulator. * Finally, the CADClientView is initialized, the model-view and observer * relationships are established, and the view is shown.
*
* The properties file contains the following data:
* * -----------------------------------------------------------------------------
* Host Name The host name where the CAD Simulator is located.
* Port Number The port number that the CAD Simulator is bound on.
* CAD Position The integer (>= 0) position for this CAD Client.
* CAD User ID The unique user id for this CAD Client.
* Error File Filename of error logging file.
* -----------------------------------------------------------------------------
* Example File:
* CADSimulatorHost = localhost
* CADSimulatorSocketPort = 4444
* CADPosition = 1
* CADUserID = A12345
* ErrorFile = cad_client_err.txt
*
* * @author Matthew Cechini (mcechini@calpoly.edu) * @version $Date: 2009/04/17 16:27:47 $ $Revision: 1.8 $ */ public class CADClient extends UnicastRemoteObject implements CADClientInterface { /** Error logger. */ private static Logger cadClientLogger = Logger.getLogger("tmcsim.client"); /** * Enumeration containing properties name values. See CADClient class * description for more information. * * @author Matthew Cechini * @see CADClient */ private static enum PROPERTIES { CAD_SIM_HOST("CADSimulatorHost"), CAD_SIM_PORT("CADSimulatorSocketPort"), CAD_RMI_PORT( "CADRmiPort"), CLIENT_CAD_POS("CADPosition"), CLIENT_USER_ID( "CADUserID"), KEYBOARD_TYPE("KeyboardType"), DISPLAY_TYPE( "DisplayType"); public String name; private PROPERTIES(String n) { name = n; } } /** * CADClientSocket Object to handle socket communication between the Client * and CAD Simulator. */ private CADClientSocket theClientSocket; /** Instance of the CADClientModel. */ private CADClientModel theClientScreenModel; /** Instance of the CADClientView. */ private CADClientView theClientScreenView; /** * Instance of the CADCLientGUI Replaces CADClientView */ private CADClientGUI theClientGUI; /** Properties object for the CADClient class. */ private Properties cadClientProp; /** RMI interface for communication with the remote Coordinator. */ private static CoordinatorInterface theCoorInt; /** reference to itself to be used for disconnecting from CADSimulator */ private CADClientInterface client = this; private static final String CONFIG_FILE_NAME = "cad_client_config.properties"; /** * Constructor. Initialize data from parsed properties file. Create a socket * connection to the CADSimulator. The ClientScreenModel is initialized with * the input and output I/O streams for socket communication. The * ClientScreenModel registers with the CAD Simulator, using CAD position * and userID read in from the properties file. The ClientScreenView is then * created and initialized and set as an observer of the model. * * A thread is created with the runnable ClientScreenModel and is started. * When this thread is no longer alive, or the ClientScrenView and * CADClientSocket are closed. The program then exits. * * @param propertiesFile * File path (absolute or relative) to the properties file * containing configuration data. */ public CADClient(String propertiesFile) throws SimulationException, RemoteException { if (!verifyProperties(propertiesFile)) System.exit(0); connect(cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name).trim(), cadClientProp.getProperty(PROPERTIES.CAD_RMI_PORT.name).trim()); // Instantiate the Socket and Model Objects. theClientSocket = new CADClientSocket(cadClientProp.getProperty( PROPERTIES.CAD_SIM_HOST.name).trim(), Integer.parseInt(cadClientProp.getProperty( PROPERTIES.CAD_SIM_PORT.name).trim())); theClientScreenModel = new CADClientModel(); theClientScreenModel.initializeScreen(theClientSocket.getInputStream(), theClientSocket.getOutputStream()); // Register this CAD Client with the Simulation Manager theClientScreenModel.register(Integer.parseInt(cadClientProp .getProperty(PROPERTIES.CLIENT_CAD_POS.name)), cadClientProp .getProperty(PROPERTIES.CLIENT_USER_ID.name)); // Instantiate the CADScreenView and set up the model-view observer // relationship. theClientScreenView = new CADClientView(theClientScreenModel); theClientScreenView.setVisible(false); // TODO: set up model-view relationship similar to ClientView and // ScreenView // Can repurpose the old model, but may be better to copy over and // modify in parallel // This is required to perform powerline commands on the data theClientGUI = new CADClientGUI(); // Each screen of the UI should have a reference to either it's parent // object or the main client // This ensures they all have access to each other and the data model theClientGUI.screen = new ScreenManager(theCoorInt); theClientGUI.login = new Login(); theClientGUI.client = this; // setup keyboard settings for CAD Client if (cadClientProp.getProperty(PROPERTIES.KEYBOARD_TYPE.name).trim() .equals("CAD")) { CADEnums.CAD_KEYS.setupCADKeyboard(); } // STD else { CADEnums.CAD_KEYS.setupStandardKeyboard(); } theClientScreenModel.addObserver(theClientScreenView); // Initialize the display if (cadClientProp.getProperty(PROPERTIES.DISPLAY_TYPE.name).equals( "FULL_SCREEN")) { theClientScreenView.addWindowListener(new WindowListener() { public void windowClosed(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowClosing(WindowEvent e) { try { theClientSocket.closeSocket(); } catch (SimulationException se) { } System.exit(0); } }); theClientScreenView.initWindow(); theClientScreenView.setVisible(false); } else { JFrame cadFrame = new JFrame("CAD Client"); cadFrame.add(theClientScreenView.initBox()); cadFrame.setSize(800, 600); cadFrame.addWindowListener(new WindowListener() { public void windowClosed(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowClosing(WindowEvent e) { try { theClientSocket.closeSocket(); } catch (SimulationException se) { } System.exit(0); } }); cadFrame.setVisible(true); } // Create the CAD Client thread to run the CADClientModel Object. Thread clientThread = new Thread(theClientScreenModel); clientThread.start(); ensureProperShutdown(); } /** * Connect to the Coordinator's RMI object, and register this object for * callback with the Coordinator. * * @param hostname * Host name of the CAD Simulator. * @param portNumber * Port number of the CAD Simulator RMI communication. * @throws SimulationException * if there is an error creating the RMI connection. */ protected void connect(String hostname, String portNumber) throws SimulationException { String coorIntURL = ""; try { coorIntURL = "rmi://" + hostname + ":" + portNumber + "/coordinator"; theCoorInt = (CoordinatorInterface) Naming.lookup(coorIntURL); theCoorInt.registerForCallback(this); } catch (Exception e) { throw new SimulationException(SimulationException.CAD_SIM_CONNECT, e); } } /** * This method verifies that the CAD Simulator Host and Port values are not * null. Also, if a CAD Position or User ID do not exist in the properties * file, the user is prompted to enter values. These values are written to * the properties file. If the user cancels the process of entering these * values, the verification fails. * * @param propertiesFile * File path (absolute or relative) to the properties file * containing configuration data. * @return True if the properties file is valid, false if not. * @throws SimulationException * if there is an exception in verifying the properties file, or * if the user cancels input. */ private boolean verifyProperties(String propertiesFile) throws SimulationException { // Load the properties file. try { cadClientProp = new Properties(); cadClientProp.load(new FileInputStream(propertiesFile)); } catch (Exception e) { cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Constructor", "Exception in reading properties file.", e); throw new SimulationException(SimulationException.INITIALIZE_ERROR, e); } // Ensure that the properties file does not have null values for the // CAD Simulator's connection information. if (cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name) == null || cadClientProp.getProperty(PROPERTIES.CAD_SIM_PORT.name) == null) { cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Constructor", "Null value in properties file."); throw new SimulationException(SimulationException.INITIALIZE_ERROR); } try { // If the properties file does not specify a CAD position, prompt // the // user to select one. If the user selects a position, write the // new properties values to the file. If the user cancels, else // throw an exception. if (cadClientProp.getProperty(PROPERTIES.CLIENT_CAD_POS.name) == null) { if (getCADPosition()) cadClientProp.store(new FileOutputStream(propertiesFile), ""); else throw new SimulationException( SimulationException.INITIALIZE_ERROR); } // If the properties file does not specifiy a CAD User ID, prompt // the // user to enter a value. If the user enters a valid ID, write the // new properties values to the file. If the user cancels, else // throw an exception. if (cadClientProp.getProperty(PROPERTIES.CLIENT_USER_ID.name) == null) { if (getUserID()) cadClientProp.store(new FileOutputStream(propertiesFile), ""); else throw new SimulationException( SimulationException.INITIALIZE_ERROR); } } catch (IOException ioe) { cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Constructor", "Exception in writing to the properties file."); throw new SimulationException(SimulationException.INITIALIZE_ERROR); } // Ensure that the properties file has a valid display type if (cadClientProp.getProperty(PROPERTIES.DISPLAY_TYPE.name) == null || (!cadClientProp.getProperty(PROPERTIES.DISPLAY_TYPE.name) .equals("FULL_SCREEN") && !cadClientProp.getProperty( PROPERTIES.DISPLAY_TYPE.name).equals("FRAME"))) { cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Constructor", "Invalid display type."); throw new SimulationException(SimulationException.INITIALIZE_ERROR); } return true; } /** * This method prompts the user to select a value for the CAD position. If * the user cancels the method returns false, else the Properties object is * updated and true is returned. * * @return True if the user successfully selected a CAD position, false if * not. */ private boolean getCADPosition() { Vector positions = new Vector(); for (int i = 0; i < 10; i++) positions.add(i); Object cadPos = null; while (true) { cadPos = JOptionPane.showInputDialog(null, "Please assign this workstation a CAD position number.", "CAD Position Asignment", JOptionPane.QUESTION_MESSAGE, null, positions.toArray(), positions.get(0)); // If the user pressed cancel, confirm the exit and return false. if (cadPos == null) { if (JOptionPane .showConfirmDialog( null, "CAD Client cannot load until a valid CAD " + "position has been selected. Do you wish to " + "cancel loading the CAD Client?", "Confirm Exit", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) return false; } // Else the user selected a CAD position, exit the loop. else { break; } } cadClientProp.setProperty(PROPERTIES.CLIENT_CAD_POS.name, cadPos.toString()); return true; } /** * This method prompts the user to enter a 5-character User ID. If the user * cancels the method returns false, else the Properties object is updated * and true is returned. * * @return True if the user successfully selected a CAD position, false if * not. */ private boolean getUserID() { String cadUID = null; while (true) { cadUID = JOptionPane.showInputDialog(null, "Please assign this workstation a 6-character CAD " + "User ID.", "CAD User ID Asignment", JOptionPane.QUESTION_MESSAGE); // /If the user pressed cancel, confirm the exit and return false. if (cadUID == null) { if (JOptionPane.showConfirmDialog(null, "CAD Client cannot load until a valid User ID " + "has been entered. Do you wish to " + "cancel loading the CAD Client?", "Confirm Exit", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) return false; } // If the user does not enter a valid User ID, notify and reprompt. else if (cadUID.length() != 6) { JOptionPane.showMessageDialog(null, "The User ID must be 6 characters.", "Invalid User ID", JOptionPane.WARNING_MESSAGE); } // Else the user entered a valid value, exit the loop. else { break; } } cadClientProp.setProperty(PROPERTIES.CLIENT_USER_ID.name, cadUID); return true; } /** * Construct the CADClient with the properties file path, either from the * command line arguments or default. * * @param args * Command line arguments. */ public static void main(String[] args) { if(System.getProperty("CONFIG_DIR") == null){ System.setProperty("CONFIG_DIR", "config"); } try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); new CADClient(System.getProperty("CONFIG_DIR") + System.getProperty("file.separator") + CONFIG_FILE_NAME); } catch (Exception e) { cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Main", "Error initializing application."); JOptionPane.showMessageDialog(new JWindow(), e.getMessage(), "Error - Program Exiting", JOptionPane.ERROR_MESSAGE); System.exit(-1); } } public void refresh() { theClientGUI.screen.refreshScreens(); } public void ensureProperShutdown() { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { theCoorInt.unregisterForCallback(client); } catch (RemoteException e) { e.printStackTrace(); } } }); } }