Main Page   Packages   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members  

Server.java

Go to the documentation of this file.
00001 /**************************************************************************
00002  Copyright (c) 2000-2001, Tony Garnock-Jones
00003  All rights reserved.
00004 
00005  Redistribution and use in source and binary forms, with or without
00006  modification, are permitted provided that the following conditions are
00007  met:
00008 
00009      * Redistributions of source code must retain the above copyright
00010        notice, this list of conditions and the following disclaimer.
00011 
00012      * Redistributions in binary form must reproduce the above
00013        copyright notice, this list of conditions and the following
00014        disclaimer in the documentation and/or other materials provided
00015        with the distribution.
00016 
00017      * Neither the names of the copyright holders nor the names of this
00018        software's contributors may be used to endorse or promote
00019        products derived from this software without specific prior
00020        written permission.
00021 
00022  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00025  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
00026  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00027  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00028  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00029  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00030  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00031  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 **************************************************************************/
00034 
00035 package org.hebe.mps;
00036 
00037 import java.util.*;
00038 import java.io.*;
00039 import java.net.*;
00040 
00041 import org.hebe.mps.naming.Binding;
00042 
00043 /**
00044  * Implements all the code needed to get an implementation of an MPS
00045  * interface running as an MPS server. Pass in your desired objectName
00046  * and an implementation of the correct interface to the
00047  * automatically-generated subclasses of this class. It will spawn a
00048  * thread that listens for connections to the server object.
00049  *
00050  * <p>
00051  * Note that the implementation object is managed by
00052  * <i>subclasses</i> of this class, and that this class knows nothing
00053  * about the implementation of the MPS interface at all.
00054  *
00055  * @author Tony Garnock-Jones <tonyg@kcbbs.gen.nz>
00056  * @see org.hebe.mps.Proxy
00057  */
00058 public class Server implements Runnable {
00059     /**
00060      * Map used to associate an MPSIDL interface instance with an
00061      * instance of class Server.
00062      *
00063      * @see org.hebe.mps.OutputStream#writeReference
00064      */
00065     private static Hashtable allServers = new Hashtable();
00066 
00067     /**
00068      * Registers an instance of Server against the MPSIDL interface
00069      * object that it is serving. The MPSIDL compiler emits calls to
00070      * this method when compiling MPSIDL specifications down to java
00071      * skeletons.
00072      *
00073      * @param key the MPSIDL interface instance being served
00074      * @param svr the server object we are registering
00075      */
00076     protected static void registerServer(Object key, Server svr) {
00077         allServers.put(key, svr);
00078     }
00079 
00080     /**
00081      * Retrieve the Server associated with a particular MPSIDL
00082      * interface instance.
00083      *
00084      * @param key the MPSIDL interface instance we're interested in
00085      * @return an instance of Server, or null if key is not being served
00086      */
00087     public static Server serverFor(Object key) {
00088         return (Server) allServers.get(key);
00089     }
00090 
00091     ///////////////////////////////////////////////////////////////////////////
00092 
00093     /** Human-readable name for this object */
00094     private String objectName;
00095     /** Machine-readable address of this object */
00096     private String boundName;
00097 
00098     /** Server socket that clients connect to us on. */
00099     private ServerSocket serverSocket;
00100     /** Address of the interface our server socket is bound to */
00101     private InetAddress address;
00102     /** TCP Port number we're listening on */
00103     private int portNumber;
00104 
00105     /**
00106      * Set to true if we are to serve access to our implementation
00107      * object in a multithreaded way; set to false to serialize all
00108      * accesses
00109      * 
00110      * @see org.hebe.mps.Server#setMultiThreaded */
00111     private boolean multiThreaded = true;
00112 
00113     /**
00114      * Look-up-table mapping method number to method implementation.
00115      *
00116      * @see org.hebe.mps.Thunk
00117      * @see org.hebe.mps.Server#dispatch */
00118     private Vector methodTable = new Vector();
00119 
00120     /**
00121      * Create a new server named objectName. Bind to INADDR_ANY, on
00122      * any unused port number.
00123      *
00124      * @param objectName human-readable name for this server
00125      * @exception MPSException if anything goes wrong setting up the server */
00126     public Server(String objectName) throws MPSException {
00127         setup(objectName, null, 0);
00128     }
00129 
00130     /**
00131      * Create a new server named objectName. Bind to INADDR_ANY, on
00132      * the passed-in port number.
00133      *
00134      * @param objectName human-readable name for this server
00135      * @param portNumber the port to use to accept connections on
00136      * @exception MPSException if anything goes wrong setting up the server
00137      */
00138     public Server(String objectName, int portNumber)
00139         throws MPSException
00140     {
00141         setup(objectName, null, portNumber);
00142     }
00143 
00144     /**
00145      * Create a new server named objectName. Bind to a specific
00146      * interface, on the passed-in port number.
00147      *
00148      * @param objectName human-readable name for this server
00149      * @param address the InetAddress of the interface to bind to
00150      * @param portNumber the port to use to accept connections on
00151      * @exception MPSException if anything goes wrong setting up the server
00152      */
00153     public Server(String objectName, InetAddress address, int portNumber)
00154         throws MPSException
00155     {
00156         setup(objectName, address, portNumber);
00157     }
00158 
00159     /**
00160      * Return the canonical name of this server.
00161      *
00162      * @return the string representation of the canonical name
00163      */
00164     public String getBoundName() {
00165         return boundName;
00166     }
00167 
00168     /**
00169      * Performs all the steps needed to get the server started.
00170      *
00171      * @param objectName human-readable name for this server
00172      * @param address the InetAddress of the interface to bind to - null for INADDR_ANY
00173      * @param portNumber the port to use to accept connections on - 0 for any unused
00174      * @exception MPSException if anything goes wrong setting up the server
00175      */
00176     private void setup(String objectName, InetAddress address, int portNumber)
00177         throws MPSException
00178     {
00179         this.objectName = objectName;
00180         this.address = address;
00181         this.portNumber = portNumber;
00182         setup();
00183     }
00184 
00185     /**
00186      * Creates and starts the thread, once all the instance variables
00187      * have been set up.
00188      *
00189      * @exception MPSException if the specified local socket address is already taken
00190      */
00191     private void setup()
00192         throws MPSException
00193     {
00194         try {
00195             serverSocket =
00196                 (address == null) ? new ServerSocket(portNumber)
00197                     : new ServerSocket(portNumber, 50, address);
00198 
00199             boundName =
00200                 "mps:inet:" +
00201                 ((address == null) ? InetAddress.getLocalHost() : serverSocket.getInetAddress()).getHostName() +
00202                 ":" +
00203                 serverSocket.getLocalPort() +
00204                 ":0";
00205 
00206             new Thread(this).start();
00207 
00208         } catch (IOException e) {
00209             throw new MPSException("Cannot bind server socket.");
00210         }
00211     }
00212 
00213     /**
00214      * Returns true if this server is running multithreaded.
00215      * @return the current multithreadedness of this server
00216      */
00217     public boolean getMultiThreaded() {
00218         return multiThreaded;
00219     }
00220 
00221     /**
00222      * Determines whether multiple threads (one thread per client
00223      * connection, remember) may access methods on the implementation
00224      * object simultaneously. If not, all dispatches to the
00225      * implementation object go through a <code>synchronized</code>
00226      * lock.
00227      *
00228      * @param m set to true to enable parallel access, or to false to enforce serial access
00229      */
00230     public void setMultiThreaded(boolean m) {
00231         multiThreaded = m;
00232     }
00233 
00234     /**
00235      * Dispatches to a method on this MPS interface server.
00236      *
00237      * @param index the index of the method to call
00238      * @param in the inputstream used by the method to read in parameters
00239      * @param out the outputstream used by the method to return results
00240      * @exception MPSException if the method causes it to be raised,
00241      * or if the method index is out-of-range
00242      */
00243     public void dispatch(int index, InputStream in, OutputStream out)
00244         throws MPSException
00245     {
00246         //System.out.println("Dispatching " + index);
00247         try {
00248             Thunk t = (Thunk) methodTable.elementAt(index);
00249             t.action(in, out);
00250         } catch (ArrayIndexOutOfBoundsException ae) {
00251             throw new MPSException("Method number " + index + " out of range");
00252         }
00253     }
00254 
00255     /**
00256      * Register a method-handler with the server.
00257      *
00258      * @param index the method-index to use to register under
00259      * @param name the name of the method - not currently used, but useful for debugging
00260      * @param thunk the object to call when the method is invoked
00261      */
00262     public void registerMethod(int index, String name, Thunk thunk) {
00263         // We could use /name/ for debug info, later.
00264         methodTable.setSize(Math.max(index + 1, methodTable.size()));
00265         methodTable.setElementAt(thunk, index);
00266     }
00267 
00268     /**
00269      * The main body of the server thread.
00270      */
00271     public void run() {
00272         try {
00273             while (true) {
00274                 Socket sock = serverSocket.accept();
00275                 new Thread(new ConnectionThread(sock, this)).start();
00276             }
00277         } catch (IOException e) {
00278             // Stop serving.
00279             try {
00280                 serverSocket.close();
00281             } catch (IOException ioee) {
00282             }
00283 
00284             // Start up again.
00285             try {
00286                 setup();
00287             } catch (MPSException mpse) {
00288             }
00289         }
00290     }
00291 
00292     /**
00293      * This class implements the body of the thread that is run for
00294      * each client that connects to an MPS interface server.
00295      */
00296     private class ConnectionThread implements Runnable {
00297         /** Socket that connects us to our client */
00298         private Socket sock;
00299         /** Server we are acting on behalf of */
00300         private Server server;
00301 
00302         public ConnectionThread(Socket sock, Server server) {
00303             this.sock = sock;
00304             this.server = server;
00305         }
00306 
00307         /**
00308          * The main body of the thread. Read requests from the
00309          * InputStream until end-of-file, and dispatch on each request
00310          * as it comes in using the <code>dispatch</code> method in
00311          * our enclosing class, org.hebe.mps.Server.
00312          *
00313          * @author Tony Garnock-Jones <tonyg@kcbbs.gen.nz>
00314          * @see org.hebe.mps.Server
00315          */
00316         public void run() {
00317             try {
00318                 InputStream in = new InputStream(sock.getInputStream());
00319                 OutputStream out = new OutputStream(sock.getOutputStream());
00320 
00321                 while (true) {
00322                     int targetOid = in.readint();       // ignored for now. Always zero.
00323 
00324                         // clear out any junk oids not sent (in the case of
00325                         // void-returning methods)
00326                         out.resetBuffer();
00327                         // put the oid for this invocation in the output message
00328                     out.writeint(targetOid);
00329 
00330                     int i = in.readint();
00331 
00332                     //System.out.println("Method " + i);
00333 
00334                     if (server.getMultiThreaded()) {
00335                         server.dispatch(i, in, out);
00336                     } else {
00337                         synchronized(server) {
00338                             server.dispatch(i, in, out);
00339                         }
00340                     }
00341                 }
00342             } catch (MPSConnectionClosedException cce) {
00343                 // Do nothing. This will happen from time to time.
00344             } catch (Exception e) {
00345                 e.printStackTrace();
00346             } finally {
00347                 try {
00348                     sock.close();
00349                 } catch (IOException ioee) {
00350                 }
00351             }
00352         }
00353     }
00354 }

Generated at Wed Aug 15 01:05:34 2001 for mps-java by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001