|  | 
         
          |  
              The DataClient 
                program, discussed in detail on the previous 
                page, can run either as an applet or a application. Below 
                it is shown as an applet. The complete code listings of DataClient 
                and DataClientWorker 
                classes are also provided.   
               
                Note: This applet generates a 
                  security exception when it attempts to open a socket to the 
                  DataServer unless the server is 
                  running on the same machine as the applet. (Also, the host address 
                  would need to be changed to the address of the host from which 
                  the applet was downloaded.)  For your own experimentation, you should download the files 
                  for both the client and server to your machine. Then compile 
                  and run them there. (You can use the host address 127.0.0.1 
                  that points to the local machine.)  
               
                
                   
                    | DataClient 
                      Applet 
 
 |   
                    | Image of DataClient 
                        in action
 
 
 
  
                        DataClient.java 
                          - 
                          Create an interface for the user to initiate a connection 
                          with the server at the given host address. It communicates 
                          via a socket at port 2222. The user name is required 
                          for the login. A text area displays messages generated 
                          for different steps in the setup and communications 
                          with the server. The 
                          server sends a set of data for each request. The top 
                          histogram shows the values in each data channel in each 
                          set of data. The lower histogram displays a distribution 
                          of values for one channel over many sets of data. + 
                          Helper class:DataClientWorker.java 
                          - this Thread subclass communicates with the server 
                          and periodically requests a set of data. When it receives 
                          the data it passes it to the DataClient_JApplet11 parent, 
                          which then displays the data in the histograms.
 
 + 
                          Previous classes: Ch.8:Tech: 
                          HistogramStatR1.java, 
                          HistogramAdaptR1.java
 Ch.6:Tech: 
                          Histogram.java, 
                          HistPanel.java
 Ch.6:Tech: 
                          PlotPanel.java, 
                          PlotFormat.java
   |   
                    |  package 
                        DataMonitor;
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
 import java.util.*;
 import java.io.*;
 import java.net.*;
 
 /**
 *
 *  A Prototype Remote Data Monitoring 
                        System
 *
 *  DataClientdisplays data sent 
                        by DataServer. At a given time
 *  interval, DataClient requests 
                        a set of data from the server.
 *  The server sends first an integer 
                        for the number of data points
 *  to be sent and then sends that 
                        many integer values.
 *
 *  DataClient runs both as a standalone 
                        app or in a browser web page.
 *
 *  The User Interface displays several 
                        items of interest:
 *   - The host IP and username are 
                        displayed in TextFields
 *   - Stop/Stop button can interrupt 
                        and restart the connection.
 *   - A label displays the status 
                        of the connection
 *   - a TextArea displays the current 
                        data.
 *   - One histogram displays the 
                        values in each channel of a data set.
 *   - The second histogram displays 
                        the distribution of values for one
 *     channel of data. 
                        The number for the desired channel is entered
 *     into a text field.
 *
 *  The thread class fDataClientWorker 
                        is used to send  requests for
 *  data and to read the data. The 
                        fDataClientWorker consists primarily
 *  of a run () method. It calls 
                        back to the setData (int []) method
 *  to send the data set obtained 
                        fromthe server.
 **/
 public class DataClient extends JApplet
 implements ActionListener
 {
 // GUI setup
 
 // First histogram will show the data values 
                        in each set
 // obtained from the server.
 HistPanel fHistDataPanel = null;
 Histogram fHistData = null;
 
 // Second histogram plots the values of one 
                        channel in the
 // data set.
 HistPanel fHistChanPanel   = null; 
                        // Histogram of a data channel
 HistogramAdaptR1 fHistChan = null;
 
 // Initial array size in HistogramAdaptR1 
                        to hold values.
 int fHistArraySize = 1000;
 
 // UI components
 JLabel     fStatusLabel      = 
                        null;
 JTextField fHostField        = 
                        null;
 JTextField fUserNameField    = 
                        null;
 JTextField fChanField        = 
                        null;
 JTextArea  fMessageArea      = 
                        null;
 
 // Flag for whether the applet is in a browser
 // or running via the main () below.
 boolean fInBrowser=true;
 
 //Buttons
 JButton fStartButton = null;
 JButton fClearButton = null;
 JButton fExitButton  = null;
 
 // Networking setup
 
 // Properties needed for the connection setup
 Socket fServer = null;
 DataClientWorker fDataClientWorker = null;
 
 int fDataServerPort     
                        = 2222; // use 2222 as default
 String fClientPorfHostt = "";
 String fHost            = 
                        "127.0.0.1";
 String fUserName        = 
                        "Smith";
 int fChannelToMonitor   = 0;
 
 // Amount of data in each set read from the 
                        server
 int fNumDataChannels = 10;
 
 // flag for connection status
 boolean fConnected = false;
 
 // read data every 500 msecs
 int fTimeUpdate = 500;
 
 /**
 * Create a User Interface with 
                        a textarea with sroll bars
 *  and a Go button to 
                        initiate processing and a Clear button
 * to clear the textarea.
 **/
 public void init () {
 
 Container content_pane = getContentPane 
                        ();
 
 // Histograms:
 
 // First hist used to show the 
                        values in the data set
 // that is obtained in each transfer 
                        from the server.
 fHistData = new Histogram ("Data 
                        set values",
 "Channel Number",
 10, 
                        0.0, 10.0);
 fHistDataPanel = new HistPanel 
                        (fHistData);
 
 
 // Second histogram displays the 
                        distribution of values
 // for one of the data channels. 
                        Use an adaptable histogram
 // since different channels could 
                        have different data ranges.
 fHistChan = new HistogramAdaptR1 
                        ("Channel",
 "Data Units",
 50, 0.0, 50.0,
 fHistArraySize);
 fHistChanPanel = new HistPanel 
                        (fHistChan);
 
 // The two hists go onto a sub-panel.
 JPanel hists_panel = new JPanel 
                        (new GridLayout (2,1));
 hists_panel.add (fHistDataPanel);
 hists_panel.add (fHistChanPanel);
 
 // Control fields:
 
 // First provide the inputs field 
                        for the host IP
 // and for the user name.
 fHostField = new JTextField (fHost,16);
 JLabel host_label = new JLabel 
                        ("Host: ");
 host_label.setHorizontalAlignment 
                        (SwingConstants.RIGHT);
 fUserNameField    = 
                        new JTextField (fUserName,16);
 JLabel name_label = new JLabel 
                        ("User Name: ");
 name_label.setHorizontalAlignment 
                        (SwingConstants.RIGHT);
 
 // Top line of controls = host 
                        and name inputs
 JPanel ctrls_panel1 = new JPanel 
                        ();
 ctrls_panel1.add (host_label);
 ctrls_panel1.add (fHostField);
 ctrls_panel1.add (name_label);
 ctrls_panel1.add (fUserNameField);
 
 // Next line holds the buttons, 
                        and the data
 // channel number to monitor.
 fStartButton = new JButton ("Start");
 fStartButton.addActionListener 
                        (this);
 fClearButton = new JButton ("Clear");
 fClearButton.addActionListener 
                        (this);
 fExitButton = new JButton ("Exit");
 if (fInBrowser)
 fExitButton.setEnabled 
                        (false);
 else
 fExitButton.addActionListener 
                        (this);
 
 JPanel buttons_panel = new JPanel 
                        ();
 buttons_panel.add (fStartButton);
 buttons_panel.add (fClearButton);
 buttons_panel.add (fExitButton);
 
 JLabel chan_label = new JLabel 
                        ("Channel: ");
 chan_label.setHorizontalAlignment 
                        (SwingConstants.RIGHT);
 fChanField = new JTextField ("0",5);
 
 JPanel chan_panel = new JPanel 
                        ();
 chan_panel.add (chan_label);
 chan_panel.add (fChanField);
 
 fStatusLabel = new JLabel ("Disconnected");
 fStatusLabel.setForeground (Color.RED);
 fStatusLabel.setHorizontalAlignment 
                        (SwingConstants.CENTER);
 
 // Now pack the components of 
                        the second ctrls
 // line of components
 JPanel ctrls_panel2 = new JPanel 
                        ();
 ctrls_panel2.add (buttons_panel);
 ctrls_panel2.add (chan_panel);
 ctrls_panel2.add (fStatusLabel);
 
 // Put the 2 lines of controls 
                        into a sub-panel
 JPanel ctrls_panel12 = new JPanel 
                        ();
 ctrls_panel12.add (ctrls_panel1);
 ctrls_panel12.add (ctrls_panel2);
 
 fMessageArea = new JTextArea ();
 fMessageArea.setEditable (false);
 // Add to a scroll pane so that 
                        a long list of
 // computations can be seen.
 JScrollPane area_scroll_pane = 
                        new JScrollPane (fMessageArea);
 
 // Use a GridBagLayout to apportion 
                        space for the
 // controls, text area and histograms.
 JPanel main_panel = new JPanel 
                        (new GridBagLayout ());
 
 GridBagConstraints c = new GridBagConstraints 
                        ();
 c.fill = GridBagConstraints.BOTH;
 
 
 // Put ctrls at top
 c.gridx = 0;
 c.gridy = 0;
 c. weightx = 1.0;
 c. weighty = 0.05;
 main_panel.add (ctrls_panel12,c);
 
 // Put text area below the controls
 c.gridx = 0;
 c.gridy = 1;
 c. weightx = 1.0;
 c. weighty = 0.25;
 main_panel.add (area_scroll_pane, 
                        c);
 
 // Put histograms in rest of the 
                        vertical space
 c.gridx = 0;
 c.gridy = 2;
 c. weightx = 1.0;
 c. weighty = 0.70;
 c.insets = new Insets (2,2,10,2);
 main_panel.add (hists_panel, c);
 
 // Add text area with scrolling 
                        to the content_pane.
 content_pane.add (main_panel);
 
 } // init
 
 /** Respond to the buttons. **/
 public void actionPerformed (ActionEvent e) 
                        {
 Object source = e.getSource ();
 if (source == fStartButton)  {
 if (fStartButton.getText 
                        ().equals ("Start") )
 start 
                        ();
 else
 stop 
                        ();
 } else if  (source == 
                        fClearButton) {
 fHistData.clear 
                        ();
 // For 
                        adaptable histogram, clear bins
 // and 
                        also clear internal data array.
 fHistChan.reset 
                        ();
 repaint 
                        ();
 
 } else if (!fInBrowser)// Exit 
                        button
 System.exit 
                        (0);
 } // actionPerformed
 
 /**
 * Make the connection to the server. 
                        Set up the DataReader
 * and begin recording the data 
                        from the server.
 **/
 public void start (){
 
 if (fConnected) stop ();
 
 // Clear the histograms
 fHistData.clear ();
 fHistData.clear ();
 
 // Get the current values of the 
                        host IP address and
 // and the username
 fHost = fHostField.getText ();
 fUserName = fUserNameField.getText 
                        ();
 try {
 fChannelToMonitor 
                        = Integer.parseInt (fChanField.getText ());
 }
 catch (NumberFormatException ex) {
 println ("Bad channel 
                        value");
 return;
 }
 
 // Now try to connect to the DataServer
 try{
 if (connect () ) {
 // Successful 
                        so set flags and change button text
 fConnected 
                        = true;
 fStartButton.setText 
                        ("Stop");
 fStatusLabel.setText 
                        ("Connected");
 fStatusLabel.setForeground 
                        (Color.BLUE);
 
 } else {
 println 
                        ("* NOT CONNECTED *");
 fStatusLabel.setText 
                        ("Disconnected");
 fStatusLabel.setForeground 
                        (Color.RED);
 }
 }
 catch (IOException e) {
 println 
                        ("* NOT CONNECTED *");
 fStatusLabel.setText 
                        ("Disconnected");
 fStatusLabel.setForeground 
                        (Color.RED);
 }
 } // start
 
 /**
 *  Connect to the server 
                        via a socket. Throws IOException
 *  if socket connection 
                        fails.
 **/
 boolean connect () throws IOException {
 
 println ("Attempting to connect 
                        to server ...");
 
 try {
 // Connect to the 
                        server using the host IP address
 // and the port at 
                        the server location
 fServer = new Socket 
                        (fHost, fDataServerPort);
 } catch (SecurityException se) 
                        {
 println ("Security 
                        Exception:\n"+se);
 return false;
 }
 
 println ("Server connected - create 
                        worker");
 
 // Create the worker to tend to 
                        this server
 fDataClientWorker =
 new DataClientWorker 
                        (this, fServer, fUserName);
 fDataClientWorker.start ();
 
 return true;
 } // connect
 
 /** Stop the worker thread. **/
 public void stop () {
 
 // Disconnect and kill the fDataClientWorker 
                        thread
 fDataClientWorker.finish ();
 setDisconnected ();
 }
 
 /** Set buttons for restart. **/
 void setDisconnected () {
 fStartButton.setText ("Start");
 fStatusLabel.setText ("Disconnected");
 fStatusLabel.setForeground (Color.RED);
 }
 
 /**
 *  The DataClientWorder 
                        passes the data array from the server.
 *  Display the data 
                        set by packing a histogram.
 *
 *  Also, plot the distribution 
                        of one of the channels of the data.
 *  The channel number 
                        is given in the text field.
 **/
 void setData (int [] data) {
 
 // Display each data 
                        set
 fHistData.pack (data, 
                        0, 0, 0.0, (double) (data.length) );
 fHistDataPanel.getScaling 
                        ();
 
 // Plot the distribution 
                        of one of the channels in the data.
 if (fChannelToMonitor 
                        >= 0 && fChannelToMonitor < data.length) {
 fHistChan.setTitle 
                        ("Channel "+ fChannelToMonitor);
 fHistChan.add 
                        ((double)data[fChannelToMonitor]);
 // 
                        Adapt since data varies from channel to channel.
 fHistChan.rebin 
                        ();
 // 
                        Now rescale and draw.
 fHistChanPanel.getScaling 
                        ();
 }
 repaint ();
 } // setData
 
 
 /**  Convenience method for sending 
                        messages to the  text area. **/
 public void println (String str) {
 fMessageArea.append (str +"\n");
 repaint ();
 }
 
 /** Create the applet and add to frame. **/
 public static void main (String[] args) {
 //
 int frame_width=500;
 int frame_height=650;
 
 // Create standalone version
 DataClient applet = new DataClient 
                        ();
 applet.fInBrowser = false;
 applet.init ();
 
 // Following anonymous class used 
                        to close window & exit program
 JFrame f = new JFrame ("Data Clent");
 f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
 
 // Add applet to the frame
 f.getContentPane ().add ( applet);
 f.setSize (new Dimension (frame_width,frame_height));
 f.setVisible (true);
 } // main
 
 } // class DataClient
 |   
                    | package DataMonitor; 
 import java.io.*;
 import java.net.*;
 
 /**
 * DataClient creates an instance of this thread 
                      class to tend to the
 * connectionto the data server.
 *
 * It periodically requests a set of data from 
                      the server and then
 * sends the data to the parent DataClient, which 
                      will update its
 * display.
 **/
 public class DataClientWorker extends Thread
 {
 // Flag for data running.
 boolean fKeepRunning = true;
 
 DataClient fDataClient;
 
 // Networking refs
 Socket fServer;
 String fUserName;
 
 // I/O with the server
 BufferedReader  fNetInputReader ;
 DataInputStream fNetInputDataStream;
 OutputStream    fNetOutputDataStream 
                      ;
 PrintWriter     fPrintWriter;
 
 // Number of data values
 int fNumChannels = -1;
 // Data array
 int [] fData = null;
 
 
 /** Receive the DataClient to provide with the 
                      data from
 * the server.
 **/
 public DataClientWorker (DataClient c, Socket 
                      server, String userName) {
 fDataClient = c;
 fServer     
                      = server;
 fUserName   = userName;
 }
 
 /** Remain in a loop to monitor the I/O from 
                      the server.
 * Display the data.
 **/
 public void run () {
 
 // The socket connection was made 
                      by the caller, now
 // set up the streams and do a login
 try {
 if (!doConnection 
                      ()){
 fDataClient.println 
                      ("  Connection/login failed");
 return;
 }
 }
 catch (IOException ioe) {
 fDataClient.println 
                      ("  I/O exception with serve:"+ ioe);
 }
 
 int num_channels = -1;
 
 // This loops until either the connection 
                      is broken or the
 //stop button or stop key is hit
 while  (fKeepRunning) 
                      {
 
 // Ask the server to 
                      send data.
 try {
 writeNetOutputLine 
                      ("  send data");
 }
 catch  (IOException 
                      e){
 break;
 }
 
 // First number sent 
                      from server is an integer that gives
 // the number of data 
                      values to be sent.
 try {
 num_channels 
                      = readNetInputInt ();
 }
 catch  (IOException 
                      e) {
 break;
 }
 
 if (num_channels != 
                      fNumChannels) {
 fNumChannels 
                      = num_channels;
 fDataClient.println 
                      ("  Number data channels = "
 + fNumChannels);
 }
 
 if (fNumChannels < 1){
 fDataClient.println 
                      ("  no data");
 break;
 }
 
 // Create an array to 
                      hold the data if not available
 if (fData == null || 
                      fNumChannels != fData.length)
 fData 
                      = new int[fNumChannels];
 
 
 for (int i=0; i < fNumChannels; 
                      i++) {
 try {
 fData[i] 
                      = readNetInputInt ();
 // 
                      Pass the data to the parent program
 fDataClient.setData 
                      (fData);
 }
 catch  (IOException 
                      e) {
 fDataClient.println 
                      ("IO Exception while reading data");
 break;
 }
 }
 
 // Ask for data every 
                      TimeUpdate
 try {
 Thread.sleep 
                      (fDataClient.fTimeUpdate);
 }
 catch (InterruptedException 
                      e)
 {}
 
 }
 
 if  (fServer != null) 
                      closeServer ();
 fDataClient.println ("disconnected");
 fDataClient.setDisconnected ();
 
 } // run
 
 /** Set up the streams with the server and then 
                      login. **/
 boolean doConnection () throws IOException {
 
 // Get the input and output streams 
                      from the socket
 InputStream in = fServer.getInputStream 
                      ();
 
 // Use the reader for obtaining 
                      text
 fNetInputReader = new BufferedReader 
                      (
 new InputStreamReader 
                      (in)) ;
 
 // User the DataInputStream for 
                      getting numerical values.
 fNetInputDataStream = new DataInputStream 
                      ( in );
 
 // Output stream for sending messages 
                      to the server.
 fNetOutputDataStream = fServer.getOutputStream 
                      ();
 
 // Write with a PrintWriter for 
                      sending text to the server.
 fPrintWriter= new PrintWriter (
 new OutputStreamWriter 
                      (fNetOutputDataStream, "8859_1"), true );
 
 // Now try the login procedure.
 if (!login ()) return false;
 
 return true;
 } // doConnection
 
 /** Here is a homemade login protocol. A password 
                      could
 * easily be added.
 **/
 boolean login () {
 
 fDataClient.println ("Waiting for 
                      login prompt...");
 
 String msg_line=readNetInputLine 
                      ();
 if (msg_line == null) return false;
 fDataClient.println (msg_line);
 if (!msg_line.startsWith ("Username:")) 
                      return false;
 
 fDataClient.println ("Send username 
                      " + fUserName);
 try {
 writeNetOutputLine (fUserName);
 }
 catch IOException e) {
 return false;
 }
 catch (Exception e) {
 fDataClient.println 
                      ("Error occurred in sending username!");
 return false;
 }
 
 fDataClient.println ("Waiting for 
                      response...");
 
 msg_line=readNetInputLine ();
 if (msg_line == null) return false;
 fDataClient.println (msg_line);
 
 return true;
 } // login
 
 /** Do all of the steps needed to stop the connection. 
                      **/
 public void finish (){
 
 // Kill the thread and stop the 
                      server
 fKeepRunning = false;
 closeServer ();
 }
 
 /** Close the socket to the server. **/
 void closeServer () {
 if (fServer == null) return;
 try {
 fServer.close ();
 fServer = null;
 }
 catch (IOException e)
 {}
 }
 
 /**
 * The net input stream is wrappped in 
                      a DataInputStream
 * so we can use readLine, readInt and 
                      readFloat
 **/
 String readNetInputLine () {
 try {
 return fNetInputReader.readLine 
                      ();
 }
 catch (IOException e) {
 return null;
 }
 }
 
 /** Read an integer value from the socket stream 
                      **/
 int readNetInputInt () throws IOException {
 return fNetInputDataStream.readInt 
                      ();
 }
 
 /**  Read float value from the socket 
                      stream. **/
 float readNetInputFloat () throws IOException 
                      {
 return fNetInputDataStream.readFloat 
                      ();
 }
 
 /**
 * The net output is a PrintWriter 
                      class which doesn't throw
 * IOException itself. Instead we 
                      have to use the PrintWriter
 * checkError () method and throw 
                      an exception ourselves if there
 * was an output error.
 **/
 void writeNetOutputLine (String string) throws 
                      IOException {
 
 fPrintWriter.println (string);
 if (fPrintWriter.checkError ()) 
                      throw (new IOException ());
 
 fPrintWriter.flush ();
 if (fPrintWriter.checkError ()) 
                      throw (new IOException ());
 } // writeNetOutputLine
 
 } // class DataClientWorker
 
 |      Last update: Dec. 11, 2004 |  |  |