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 }