package server; import client.ClientDataInterface; import java.rmi.*; import java.rmi.server.*; import javax.swing.*; import java.awt.*; /**** * * Class Server is a simple illustration of a remote server that uses RMI * communication. The server provides three typical methods to a client: * <tt>receiveClientInput</tt>, <tt>compute</tt>, and <tt>getServerOutput</tt>. * These methods are described further in their respective method * documentation. * <p> * The <tt>main</tt> method of the server accepts two command-line arguments. * The first argument is the name of the host computer on which the server * runs. The hostname may be suffixed with a colon-delimited port number. If * the port number is not present, the rmiregistry on the host is assumed to be * running on the well-known port 1099. So, e.g., a legal hostname could be * "waldorf.csc.calpoly.edu" or "waldorf.csc.calpoly.edu:1098", the latter * selecting alternate registry port 1098. * <p> * The second command-line argument is an optional flag indicating if the * server's GUI should be displayed when it runs. If the argument is missing, * the default behavior is to display the GUI. If the second argument is * "<tt>-nd</tt>", then no GUI display is shown. Using the "-nd" argument is * useful when the server is run in a context where there is no support for a * Java GUI display, for example, running the server on a UNIX machine like * waldorf when logged into waldorf via telnet from a Windows PC. * <p> * See the example <a href= ../client/Client.html> <tt>Client</tt> </a> class * for a description of how a client connects to this server and uses its * services. * <p> * It is noteworthy in this example that all method calls are initiated from * the client. I.e., the computation is 100% client-driven. The server calls * <emphasis>no</emphasis> client methods. Specifically, input data are * <emphasis>pushed</emphasis> from the client to the server by the client's * call to <tt>receiveClientInput</tt>. The server computation is initiated by * the client's call to <tt>compute</tt>. And then the output data are * <emphasis>pulled</emphasis> by the client from the server with the client's * call to <tt>getServerOutput</tt>. * <p> * This client-driven computation could be replaced with a server-driven form, * where the server calls client's methods. Or there could be some combined * form, e.g., the client performs a push of its input data, and the server * responds with a push its output. Whether to use a client- or sever-driven * form of computation depends on a number of factors, including where the * end-user fits into the computation, which side should perform input data * validity checking, and what security concerns there are. Details of these * issues are beyond the scope of this example. * */ public class Server extends UnicastRemoteObject implements ServerInterface { /** * Construct this by calling the parent constructor. */ public Server() throws RemoteException { super(); } /*-* * Public client methods. */ /** * Receive some input from the client. Store a local copy for computation * use. Also, display the data in the server's display window, or output * the data to stdout if no display is available. */ public void receiveClientInput(ClientDataInterface clientData) throws RemoteException { this.clientData = clientData; if (displayOn) { textField.setText(Integer.toString(clientData.getValue())); } else { System.out.println("Received Client Data: " + Integer.toString(clientData.getValue())); } } /** * Perform some computation using the client input. In this case, we * simply add 1 to the numeric input value received by the * <tt>receiveClientInput</tt> method. */ public void compute() throws RemoteException { serverOutput = clientData.getValue() + 1; } /** * Return computation results back to the client, when the client calls for * it. */ public Object getServerOutput() throws RemoteException { return new Integer(serverOutput); } /*-* * Protected setup methods. */ /** * Set up the display as a JFrame with a labeled text field. The * </tt>hostname</tt> input is the command-line argument from main. */ protected static void setupDisplay(String hostname) { /* * Allocate the frame and its innards. */ JFrame frame = new JFrame(); JPanel panel = new JPanel(); Box box = Box.createHorizontalBox(); /* * Add the text field. */ textField = new JTextField(8); box.add(new JLabel("Received Client Data: ")); box.add(textField); /* * Add border for some spacing. */ panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); panel.add(box, BorderLayout.CENTER); /* * Add to, pack, and make visible the frame. */ frame.setTitle(hostname + " server"); frame.getContentPane().add(panel); frame.pack(); frame.setVisible(true); } /** * Perform the necessary setup for remote execution. The <tt>hostname</tt> * input is a server name with optional port number, e.g., * "<tt>waldorf.csc.calpoly.edu</tt>" or * "<tt>waldorf.csc.calpoly.edu:1098</tt>". Server setup entails the * following processing: * <br><br> <ol><li> * Set up a Java security manager if there isn't one already running on * the server machine. * <br><br><li> * Create a server name with the syntax * "<tt>//<em>hostname</em>/Server</tt>". The "<tt>//</tt>" prefix is * required by the <tt>Naming.rebind</tt> method. The * <tt><em>hostname</em></tt> is the given string argument. The * "<tt>/Server</tt>" suffix is the name of the server class, i.e., this * class. * <br><br><li> * Allocate a remote server instance and bind it in the host's rmi * registry. This makes the server available to requesting clients. * <br><br><li> * Handle any exception that may occur during server binding. The most * typical problems include that the server is not running or that the * security policy is not met. The easiest way to avoid security policy * problems is to use the most liberal policy possible (see the file * ./java.policy.liberal). * </ol> */ protected static void setupServer(String hostname) { /* * Set up the security manager if necessary. */ if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } /* * Allocate a remote server instance and bind it in the host's rmi * registry. This makes the server available to requesting clients. */ try { Server server = new Server(); Naming.rebind("//" + hostname + "/Server", server); System.out.println("Server bound"); } /* * Handle any remote exception that may occur during binding. */ catch (Exception e) { System.err.println("Server binding exception:\n" + e.getMessage()); e.printStackTrace(); } } /** * Get the hostname from the command-line argument, exiting if there is * none. Then call the display and server setup methods and we're ready to * accept client requests. */ public static void main(String[] args) { /* * Make sure there's a hostname given on the command line. */ if (args.length == 0) { System.out.println( "First command-line argument must be a host name."); System.exit(0); } /* * Set up the display if the second command-line arg is != "-nd". */ if ((args.length > 1) && (args[1].equals("-nd"))) { displayOn = false; } else { displayOn = true; setupDisplay(args[0]); } /* * Set up the server. */ setupServer(args[0]); } /** Local copy of client input for computing with */ protected ClientDataInterface clientData; /** Value computed by the server */ protected int serverOutput; /** The text field that displays the received client data; received client data are printed to stdout instead of this text field if the GUI display is not active */ protected static JTextField textField; /** True if the display is on, i.e., the 2nd command-line arg != "-nd" */ protected static boolean displayOn; }