diff options
author | eea1 <eea1@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-05-12 19:59:44 +0000 |
---|---|---|
committer | eea1 <eea1@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-05-12 19:59:44 +0000 |
commit | 2fa8a8d25159c89e0ce4db26dc8b187b01236f41 (patch) | |
tree | c6bf6b5402b999b722c11b83cc28fd63ff414379 /java | |
parent | ea2e1d76a4f50a3e99fdd4978be57fdecf5711d8 (diff) | |
download | ATCD-2fa8a8d25159c89e0ce4db26dc8b187b01236f41.tar.gz |
Added the Naming service to the repository (again?). There may be a problem
since this has always been in the release archives, but somehow didn't stick
in CVS.
Diffstat (limited to 'java')
-rw-r--r-- | java/netsvcs/Naming/Makefile | 24 | ||||
-rw-r--r-- | java/netsvcs/Naming/NameAcceptor.java | 306 | ||||
-rw-r--r-- | java/netsvcs/Naming/NameHandler.java | 521 | ||||
-rw-r--r-- | java/netsvcs/Naming/NameProxy.java | 351 | ||||
-rw-r--r-- | java/netsvcs/Naming/NameReply.java | 145 | ||||
-rw-r--r-- | java/netsvcs/Naming/NameRequest.java | 331 |
6 files changed, 1678 insertions, 0 deletions
diff --git a/java/netsvcs/Naming/Makefile b/java/netsvcs/Naming/Makefile new file mode 100644 index 00000000000..af49d9061d8 --- /dev/null +++ b/java/netsvcs/Naming/Makefile @@ -0,0 +1,24 @@ +# Makefile +# $Id$ + +.SUFFIXES: .java .class + +JACE_WRAPPER = $(WRAPPER_ROOT)/java + +all: + javac -d ${JACE_WRAPPER}/classes $(files) +doc: + javadoc -d ${JACE_WRAPPER}/doc $(files) $(packages) + +files = NameAcceptor.java \ + NameHandler.java \ + NameRequest.java \ + NameReply.java \ + NameProxy.java \ + +packages = netsvcs \ + netsvcs.Naming + +realclean: + /bin/rm -rf ${JACE_WRAPPER}/classes/netsvcs/Naming + diff --git a/java/netsvcs/Naming/NameAcceptor.java b/java/netsvcs/Naming/NameAcceptor.java new file mode 100644 index 00000000000..f704504ee3c --- /dev/null +++ b/java/netsvcs/Naming/NameAcceptor.java @@ -0,0 +1,306 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Naming + * + * = FILENAME + * NameAcceptor.java + * + * Listens on the specified port (command line option) and launches + * NameHandlers when connections are made. Each NameHandler runs in + * its own thread. + * + * The hash table for the mapping and a timer queue are created here. + * Periodically the mapping is written out to a file. + * + * A small main program is included to start things off. If the + * data file exists, it is read into memory. Currently the service + * stores the entire mapping in memory at all times. The mapping is + * dumped to a file at regular intervals. + * + *@see netsvcs.Naming.NameHandler + * + *@author Everett Anderson + * + *************************************************/ +package netsvcs.Naming; + +import java.io.*; +import java.net.*; +import java.util.*; +import JACE.OS.*; +import JACE.Misc.*; +import JACE.Connection.*; +import JACE.Reactor.*; +import JACE.ASX.TimeValue; + +public class NameAcceptor extends Acceptor implements Runnable +{ + /** + * Constructor + */ + public NameAcceptor () + { + super(); + + // Create the hash table and timer queue + this.mapping_ = new Hashtable(); + this.tq_ = new TimerQueue(true); + } + + /** + * Simple main program. Command line options are + * described under parseArgs. + */ + public static void main (String [] args) + { + // Simple main program to get things rolling + NameAcceptor na = new NameAcceptor(); + + na.init(args); + } + + + /** + * Close the socket when shutting down + */ + public int fini () + { + try + { + this.done_ = true; + this.sockAcceptor_.close(); + } + catch (IOException e) + { + ACE.ERROR("" + e); + return -1; + } + + return 0; + } + + /** + * Stops accepting when suspended + */ + public int suspend() + { + this.suspended_ = true; + return 0; + } + + /** + * Resumes accepting + */ + public int resume() + { + this.suspended_ = false; + return 0; + } + + + /** + * Runs this instance in its own thread + */ + public int init (String [] args) + { + // Parse arguments + this.parseArgs (args); + + System.out.println("Starting naming service on port: " + this.port_); + + // Run in own thread of control so that we don't block the caller + new Thread (this).start(); + + return 0; + } + + /** + * + * Main loop: launches NameHandlers in separate threads whenever a + * connection request is made + */ + public void run () + { + // Load the hash table from disk + this.loadTable(); + + // Schedule to write out the memory copy of the hash table at regular + // intervals + this.tq_.scheduleTimer(this, + null, + new TimeValue(this.updateInterval_), + new TimeValue(this.updateInterval_)); + + try + { + // Create new NameHandlers as requests come in + this.open (this.port_); + while (!this.done_) { + + if (!this.suspended_) + this.accept (); + } + } + catch (SocketException e) + { + ACE.ERROR ("Socket Exception: " + e); + } + catch (InstantiationException e) + { + ACE.ERROR (e); + } + catch (IllegalAccessException e) + { + ACE.ERROR (e); + } + catch (IOException e) + { + ACE.ERROR (e); + } + } + + /** + * Create a new NameHandler + */ + protected SvcHandler makeSvcHandler () + throws InstantiationException, IllegalAccessException + { + return new netsvcs.Naming.NameHandler (this.mapping_); + } + + /** + * Process the command line. The following options are available: + * + * -p <port> Port number for listening + * -f <filename> Name of the database file + * -t <time> Mapping write-out time interval (in seconds) + * + */ + protected void parseArgs (String args[]) + { + String s; + GetOpt opt = new GetOpt (args, "p:f:t:"); + for (int c; (c = opt.next ()) != -1; ) + { + switch (c) + { + // Specify port + case 'p': + s = opt.optarg (); + this.port_ = (new Integer (s)).intValue (); + break; + // Specify file name of the database + case 'f': + s = opt.optarg (); + this.filename_ = new String(s); + break; + // Specify time interval to write out the table + case 't': + s = opt.optarg (); + this.updateInterval_ = (new Integer (s)).intValue(); + break; + default: + ACE.ERROR ("Unknown argument: " + c); + break; + } + } + } + + /** + * Loads the hash table into memory from the specified + * file. Uses ObjectInputStream. + */ + protected void loadTable () + { + File file = new File(this.filename_); + FileInputStream fis; + ObjectInputStream ois; + + Hashtable ht = null; + + try { + + if ((file.exists()) && (file.canRead())) { + + fis = new FileInputStream (file); + + ois = new ObjectInputStream(fis); + + ht = (Hashtable)ois.readObject(); + } else + return; + } catch (ClassNotFoundException e) { + ACE.ERROR(e); + } catch (StreamCorruptedException e) { + ACE.ERROR(e); + } catch (SecurityException e) { + ACE.ERROR(e); + } catch (IOException e) { + ACE.ERROR(e); + } + + if (ht != null) + this.mapping_ = ht; + + } + + /** + * Writes the table out to the specified file. + */ + protected void saveTable () + { + FileOutputStream fos; + ObjectOutputStream oos; + + try { + + fos = new FileOutputStream(this.filename_); + oos = new ObjectOutputStream(fos); + + oos.writeObject(this.mapping_); + + oos.flush(); + + oos.close(); + + } catch (OptionalDataException e) { + ACE.ERROR(e); + } catch (NotSerializableException e) { + ACE.ERROR(e); + } catch (IOException e) { + ACE.ERROR(e); + } + } + + /** + * Call back for the TimerQueue. This calls the method to save the + * hash table. The default time out is 60 seconds. + */ + public int handleTimeout (TimeValue tv, Object obj) + { + this.saveTable(); + + return 0; + } + + // Port to listen on + private int port_ = ACE.DEFAULT_SERVER_PORT; + + // Mapping data structure + Hashtable mapping_ = null; + + // Default file name + String filename_ = "namedata.dat"; + + // How often to save the table (seconds) + int updateInterval_ = 60; + + // Calls handleTimeout at updateInterval_ intervals + TimerQueue tq_ = null; + + boolean done_ = false; + boolean suspended_ = false; + +} + diff --git a/java/netsvcs/Naming/NameHandler.java b/java/netsvcs/Naming/NameHandler.java new file mode 100644 index 00000000000..a619eab0733 --- /dev/null +++ b/java/netsvcs/Naming/NameHandler.java @@ -0,0 +1,521 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Naming + * + * = FILENAME + * NameHandler.java + * + * An instance of this class is created in a separate thread for each connection + * request received by the NameAcceptor. All interaction between the + * client's requests and the database are handled here. + * + * In general, the user binds a name to a (value, type) pair. The type is just + * treated as just another String (in the C++ version the name and value are + * arrays of 16 bit data types and the type is an array of 8 bit chars). + * + * For this to work in the hash table scheme, the type and value are wrapped in + * a ValueType class defined at the end of this file. + * + * This is compatible with the C++ ACE remote name service. + * + *@see netsvcs.Naming.NameAcceptor + *@see netsvcs.Naming.NameRequest + *@see netsvcs.Naming.NameReply + * + *@author Everett Anderson + * + *************************************************/ +package netsvcs.Naming; + +import java.io.*; +import java.util.*; +import JACE.OS.*; +import JACE.Connection.*; +import JACE.Reactor.*; +import JACE.SOCK_SAP.*; + +public class NameHandler extends SvcHandler +{ + /** + * Constructor + * + * @param mapping Hash table created in NameAcceptor + */ + public NameHandler (Hashtable mapping) + { + super(); + + this.mapping_ = mapping; + } + + /** + * Starts this handler in its own thread + * + */ + public int open (Object obj) + { + new Thread (this).start (); + return 0; + } + + /** + * Main loop that this thread executes. Waits for connection requests and + * creates a NameHandler thread for each. + * + */ + public void run () + { + ACE.DEBUG("NameHandler instance running"); + + // Can't assume the SOCKStream uses DataInputStream, so put one + // over its OutputStream + DataInputStream dis = new DataInputStream (this.peer().inputStream()); + + // The NameRequest is the how all requests come in to the naming service. + NameRequest nameRequest = new NameRequest(); + + // Main loop -- wait for requests + int msgLen; + try + { + while (!this.done_) + { + // Read a NameRequest from the stream + nameRequest.streamInFrom(dis); + + // Decide what to do based on the request type + this.dispatch(nameRequest); + + } + } + catch (NullPointerException e) + { + ACE.ERROR ("Connection reset by peer"); + } + catch (EOFException e) + { + /* The client has shut down the connection */ + + } + catch (IOException e) + { + ACE.ERROR (e); + } + finally + { + try + { + this.peer ().close (); + } + catch (IOException e) + { + } + } + } + + + /** + * + * This is the point at which a request is sent to the various methods + * that fulfill it. Switches on the request type -- bind, rebind, resolve, + * etc. + * + *@param nameRequest The request to fill + */ + void dispatch(NameRequest nameRequest) throws IOException + { + + // Call the various other member functions based on the + // message type of the request -- bind, rebind, etc. + switch (nameRequest.requestType()) + { + case NameRequest.BIND: + this.bind(nameRequest, false); + break; + case NameRequest.REBIND: + this.bind(nameRequest, true); + break; + case NameRequest.RESOLVE: + this.resolve(nameRequest); + break; + case NameRequest.UNBIND: + this.unbind(nameRequest); + break; + case NameRequest.LIST_NAMES: + this.listByName(nameRequest.name(), false); + break; + case NameRequest.LIST_VALUES: + this.listByValue(nameRequest.name(), false); + break; + case NameRequest.LIST_TYPES: + this.listByType(nameRequest.name(), false); + break; + case NameRequest.LIST_NAME_ENTRIES: + this.listByName(nameRequest.name(), true); + break; + case NameRequest.LIST_VALUE_ENTRIES: + this.listByValue(nameRequest.name(), true); + break; + case NameRequest.LIST_TYPE_ENTRIES: + this.listByType(nameRequest.name(), true); + break; + default: + System.err.println("unknown type"); + + ACE.ERROR("Unknown type: " + nameRequest.requestType()); + + // Send a failure message. This will only work if the other + // side is expecting something like a NameReply rather than + // a NameRequest. It would've been better to have everything + // use NameRequests to avoid this kind of thing. + NameReply reply = new NameReply(NameReply.FAILURE, 0); + reply.streamOutTo(this.peer()); + + break; + } + + } + + /** + * + * Bind a name and a (value, type) pair. All this data is given in the + * NameRequest from the client. Returns a NameReply back to the client + * with either Reply.SUCCESS or Reply.FAILURE as the type. + * + *@param request NameRequest given by the client + *@param rebind Is this a rebind or not? + */ + void bind (NameRequest request, boolean rebind) throws IOException + { + // The hash table entries consists of (String name, ValueType data) pairs, so + // create the appropriate ValueType + ValueType vt = new ValueType(request.type(), + request.value()); + + // Reply to tell sender of success or failure + NameReply reply = new NameReply(); + + // If it's a rebind request, overwrite the old entry. If the key doesn't + // exist, add it. If it does exist and it's not a bind request, return + // a failure code via a NameReply. + if ((rebind) || (!this.mapping_.containsKey(request.name()))) { + + System.err.println("Binding: " + request.name() + " and " + vt.value_); + + // Add/Update the entry in the hash table + this.mapping_.put(request.name(), vt); + + // Set the reply code to success + reply.type(NameReply.SUCCESS); + + } else { + + ACE.DEBUG("Key " + request.name() + " already exists"); + + // Set reply code to failure + reply.type(NameReply.FAILURE); + + // reply error code unused as far as I know + } + + reply.streamOutTo(this.peer()); + } + + /** + * Given a name, this looks up and returns the type and value. This is + * done by sending back a full NameRequest with the correct info. If + * there is a problem, an "empty" NameRequest is returned -- it has no + * name, type, or value fields. + * + *@param request NameRequest sent by the client (has the name to lookup) + */ + void resolve (NameRequest request) throws IOException + { + // A NameRequest is also used in response + NameRequest result; + + // Wrap a DataOutputStream around the socket's output stream + // (the socket should already have at least a BufferedOutputStream) + DataOutputStream dos = new DataOutputStream(this.peer().outputStream()); + + // If the requested name is in the hash table, return the data + if (this.mapping_.containsKey(request.name())) { + + // Get the data pair based on the name + ValueType vt = (ValueType)this.mapping_.get(request.name()); + + ACE.DEBUG("Good resolve: " + vt.value_); + + // Fill the reply structure + result = new NameRequest(NameRequest.RESOLVE, + null, + vt.value_, + vt.type_, + null); + + } else { + + // Otherwise return a null response + result = new NameRequest(NameRequest.RESOLVE, + null, + null, + null, + null); + + } + + // Send the result to the socket + // result.streamOutTo(dos); + + result.streamOutTo(this.peer()); + + } + + /** + * + * Given a name, remove its entry in the mapping. Returns a NameReply + * to the client with NameReply.SUCCESS or NameReply.FAILURE. + * + *@param request NameRequest from the client (has the name to remove) + */ + void unbind (NameRequest request) throws IOException + { + NameReply reply = new NameReply(); + + // If the given key isn't in the table, return an error + // Otherwise remove it. Uses a NameReply to respond. + if (!this.mapping_.containsKey(request.name())) + reply.type(NameReply.FAILURE); + else { + this.mapping_.remove(request.name()); + reply.type(NameReply.SUCCESS); + } + + // Send the reply out to the socket + reply.streamOutTo(this.peer()); + } + + /** + * + * Given a pattern string (given in NameRequest's name field), this + * finds all the entries in the mapping which have a name that begins with + * the string. Each one is sent back separately via a NameRequest, and this + * sequence is followed by a blank NameRequest. + * + *@param pattern Pattern to find (what result names should begin with) + *@param completeLookup Should the value and type be returned as well? + */ + void listByName (String pattern, boolean completeLookup) throws IOException + { + // Get a listing of all the keys in the hash table + Enumeration enum = this.mapping_.keys(); + + // References used in the loop + String name; + ValueType vt; + + // A NameRequest is used to return each item corresponding to the pattern. + NameRequest result = new NameRequest((completeLookup ? NameRequest.LIST_NAMES : + NameRequest.LIST_NAME_ENTRIES), + null, + null, + null, + null); + + // Keep ourselves safe from null pointer exceptions + if (pattern == null) + pattern = new String(""); + + // Scan through all the elements + while (enum.hasMoreElements()) { + + // Get a key + name = (String)enum.nextElement(); + + // Does it fit the pattern? + if (name.startsWith(pattern)) { + + // Set the result name + result.name(name); + + // Only make another hash table request if the user + // wants all the data + if (completeLookup) { + + // Get data from the hash table + vt = (ValueType)mapping_.get(name); + + // Set the rest of the data + result.type(vt.type_); + result.value(vt.value_); + } + + // Send it to the socket + result.streamOutTo(this.peer()); + } + } + + // Send final null message + result.name(null); + result.type(null); + result.value(null); + result.requestType(NameRequest.MAX_ENUM); + result.streamOutTo(this.peer()); + } + + /** + * + * Given a pattern string (given in NameRequest's name field), this + * finds all the entries in the mapping which have a type that begins with + * the string. Each one is sent back separately via a NameRequest, and this + * sequence is followed by a blank NameRequest. + * + *@param pattern Pattern to find (what result types should begin with) + *@param completeLookup Should the value be returned as well? This is only + * used to decide between LIST_TYPES and LIST_TYPE_ENTRIES + * since we might as well send back both if we look them up + * together. + */ + void listByType (String pattern, boolean completeLookup) throws IOException + { + // Get a listing of all the keys in the hash table + Enumeration enum = this.mapping_.keys(); + + // References used in the loop + String name; + ValueType vt; + + // A NameRequest is used to return each item corresponding to the pattern. + NameRequest result = new NameRequest((completeLookup ? NameRequest.LIST_TYPES : + NameRequest.LIST_TYPE_ENTRIES), + null, + null, + null, + null); + // Keep ourselves safe from null pointer exceptions + if (pattern == null) + pattern = new String(""); + + // Scan through all the elements + while (enum.hasMoreElements()) { + + // Get a key + name = (String)enum.nextElement(); + + // Have to get all the data for this entry to compare + vt = (ValueType)mapping_.get(name); + + // Does it fit the pattern? + if (vt.type_ != null) + if (vt.type_.startsWith(pattern)) { + + // Set the result values + result.name(name); + result.type(vt.type_); + result.value(vt.value_); + + // Send it out to the socket + result.streamOutTo(this.peer()); + } + } + + // Send final null message + result.name(null); + result.type(null); + result.value(null); + result.requestType(NameRequest.MAX_ENUM); + result.streamOutTo(this.peer()); + } + /** + * + * Given a pattern string (given in NameRequest's name field), this + * finds all the entries in the mapping which have a value that begins with + * the string. Each one is sent back separately via a NameRequest, and this + * sequence is followed by a blank NameRequest. + * + *@param pattern Pattern to find (what result values should begin with) + *@param completeLookup Should the type be returned as well? This is only + * used to decide between LIST_VALUES and LIST_VALUE_ENTRIES + * since we might as well send back both if we look them up + * together. + */ + + void listByValue (String pattern, boolean completeLookup) throws IOException + { + // Get a listing of all the keys in the hash table + Enumeration enum = this.mapping_.keys(); + + // References used in the loop + String name; + ValueType vt; + + // A NameRequest is used to return each item corresponding to the pattern. + NameRequest result = new NameRequest((completeLookup ? NameRequest.LIST_VALUES : + NameRequest.LIST_VALUE_ENTRIES), + null, + null, + null, + null); + // Keep ourselves safe from null pointer exceptions + if (pattern == null) + pattern = new String(""); + + // Scan through all the elements + while (enum.hasMoreElements()) { + + // Get a key + name = (String)enum.nextElement(); + + // Have to get all the data for this entry to compare + vt = (ValueType)mapping_.get(name); + + // Does it fit the pattern? + if (vt.value_ != null) + if (vt.value_.startsWith(pattern)) { + + // Set the result values + result.name(name); + result.type(vt.type_); + result.value(vt.value_); + + // Send it out to the socket + result.streamOutTo(this.peer()); + } + } + + // Send final null message + result.name(null); + result.type(null); + result.value(null); + result.requestType(NameRequest.MAX_ENUM); + result.streamOutTo(this.peer()); + } + + boolean done_ = false; + + + // References to the hash table and the timer queue + Hashtable mapping_; +} + + +/** + * A simple wrapper to keep the type and value together in + * the hash table. + */ +class ValueType implements Serializable +{ + /** + * Constructor + * + *@param type Type string to include + *@param value Value string to include + */ + ValueType(String type, String value) + { this.type_ = type; this.value_ = value; } + + public String type_; + public String value_; +} + diff --git a/java/netsvcs/Naming/NameProxy.java b/java/netsvcs/Naming/NameProxy.java new file mode 100644 index 00000000000..249f745f5ce --- /dev/null +++ b/java/netsvcs/Naming/NameProxy.java @@ -0,0 +1,351 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Naming + * + * = FILENAME + * NameProxy.java + * + * This is a proxy which clients can use to interact with the naming service. They + * open a SOCKStream to the service, and can then call simple bind and resolve + * methods. + * + *@see netsvcs.Naming.NameAcceptor + *@see netsvcs.Naming.NameHandler + * + *@author Everett Anderson + * + *************************************************/ +package netsvcs.Naming; + +import java.io.*; +import java.net.*; +import java.util.*; +import JACE.OS.*; +import JACE.SOCK_SAP.*; + +public class NameProxy +{ + /** + * Constructor + * + *@param socket A SOCKStream already connected to the naming service + */ + public NameProxy(SOCKStream socket) + { + this.socket_ = socket; + } + + /** + * Attempt to bind the given data pair + * @param name Name/key + * @param value Value to bind + * + * @return True iff bind is successful + */ + public boolean bind(String name, String value) throws IOException + { + return this.bind(name, value, null, false); + } + + /** + * Attempt to bind the given data triplet + * @param name Name/key + * @param value Value to bind + * @param type Type to bind (another string) + * + * @return True iff the bind was successful + */ + public boolean bind(String name, String value, String type) throws IOException + { + return this.bind(name, value, type, false); + } + + /** + * The most generic of the bind methods. Allows factoring out of common code. Not public. + */ + boolean bind (String name, String value, String type, boolean rebind) throws IOException + { + // Create a new NameRequest with the desired info + NameRequest request = new NameRequest(rebind ? NameRequest.REBIND : NameRequest.BIND, + name, + value, + type, + null); + + // Send it to the naming service + request.streamOutTo(this.socket_); + + // Create a reply + NameReply reply = new NameReply(); + + // Get the status of the bind from the naming service + reply.streamInFrom(this.socket_); + + // Return true on success + return (reply.type() == NameReply.SUCCESS ? true : false); + } + + /** + * Rebind a name and a value + * @param name Name/key + * @param value Bound value + * + * @return True if the rebind was successful + */ + public boolean rebind (String name, String value) throws IOException + { + return this.bind(name, value, null, true); + } + + /** + * Rebind a name, value, and type + * @param name Name/key + * @param value Bound value + * @param type Bound type + * + * @return True if rebind was successful + */ + public boolean rebind (String name, String value, String type) throws IOException + { + return this.bind(name, value, type, true); + } + /** + * Look up information bound to the given key/name. + * + * @param name Name/key + * + * @return Vector with three elements: + * 0 Name/key + * 1 Value + * 2 Type + */ + public Vector resolve (String name) throws IOException + { + // Create a new NameRequest with the name & request type + NameRequest request = new NameRequest(NameRequest.RESOLVE, + name, + null, + null, + null); + + // Send it to the naming service + request.streamOutTo(this.socket_); + + // Get a response (hopefully with the value and type) + request.streamInFrom(this.socket_); + + // Dump the result into a vector + Vector result = new Vector(); + + result.addElement(request.name()); + result.addElement(request.value()); + result.addElement(request.type()); + + // Cut it down to the size we need + result.trimToSize(); + + return result; + } + + /** + * Remove the entry in the mapping corresponding to the given name/key. + * + * @param name Name/key + * + * @return True if the unbind was successful + */ + public boolean unbind (String name) throws IOException + { + NameRequest request = new NameRequest(NameRequest.UNBIND, + name, + null, + null, + null); + // Send the request to the naming service + request.streamOutTo(this.socket_); + + NameReply reply = new NameReply(); + + // Get reply + reply.streamInFrom(this.socket_); + + return (reply.type() == NameReply.SUCCESS ? true : false); + } + + /** + * Return a vector that's a list of names (Strings) that begin with + * the given pattern + * @param pattern Search pattern + * @return Vector List of names + */ + public Vector listNames (String pattern) throws IOException + { + return this.requestSimpleList(pattern, NameRequest.LIST_NAMES); + } + + /** + * Return a vector that's a list of types (Strings) that begin with + * the given pattern + * @param pattern Search pattern + * @return Vector List of types + */ + public Vector listTypes (String pattern) throws IOException + { + return this.requestSimpleList(pattern, NameRequest.LIST_TYPES); + } + + /** + * Return a vector that's a list of values (Strings) that begin with + * the given pattern + * @param pattern Search pattern + * @return Vector List of values + */ + public Vector listValues (String pattern) throws IOException + { + return this.requestSimpleList(pattern, NameRequest.LIST_VALUES); + } + + /** + * Non-public generic list gathering method + */ + Vector requestSimpleList (String pattern, int type) throws IOException + { + // Make request for a list of the given type + NameRequest request = new NameRequest(type, + pattern, + null, + null, + null); + request.streamOutTo(this.socket_); + + // Allocate and reuse the DIS here rather than each time we call + // streamInFrom + DataInputStream dis = new DataInputStream(this.socket_.inputStream()); + + request.streamInFrom(dis); + Vector result = new Vector(); + + // Add elements until there's a null message with the MAX_ENUM + // request type + while (request.requestType() != NameRequest.MAX_ENUM) { + if (type == NameRequest.LIST_NAMES) + result.addElement(new String(request.name())); + else + if (type == NameRequest.LIST_VALUES) + result.addElement(new String(request.value())); + else + result.addElement(new String(request.type())); + + request.streamInFrom(dis); + } + + // Adjust the vector to the minimal size + result.trimToSize(); + + return result; + } + + /** + * Get a vector with the entire data set for entries whose name begins with + * the given pattern. Each element in the vector is another vector + * with the following layout: + * 0 Name/key + * 1 Value + * 2 Type + * + * @param pattern Search pattern + * @return Vector of vectors + */ + public Vector listNameEntries (String pattern) throws IOException + { + return this.requestComplexList(pattern, NameRequest.LIST_NAME_ENTRIES); + } + + /** + * Get a vector with the entire data set for entries whose value begins with + * the given pattern. Each element in the vector is another vector + * with the following layout: + * 0 Name/key + * 1 Value + * 2 Type + * + * @param pattern Search pattern + * @return Vector of vectors + */ + public Vector listValueEntries (String pattern) throws IOException + { + return this.requestComplexList(pattern, NameRequest.LIST_VALUE_ENTRIES); + } + + /** + * Get a vector with the entire data set for entries whose type begins with + * the given pattern. Each element in the vector is another vector + * with the following layout: + * 0 Name/key + * 1 Value + * 2 Type + * + * @param pattern Search pattern + * @return Vector of vectors + */ + + public Vector listTypeEntries (String pattern) throws IOException + { + return this.requestComplexList(pattern, NameRequest.LIST_TYPE_ENTRIES); + } + + /** + * Non-public generic method for getting a a vector of vectors with the + * entire data set for entries fitting the given pattern. + */ + Vector requestComplexList (String pattern, int type) throws IOException + { + // Create request with desired type + NameRequest request = new NameRequest(type, + pattern, + null, + null, + null); + // Send it to the naming service + request.streamOutTo(this.socket_); + + // Allocate the DIS here and reuse + DataInputStream dis = new DataInputStream(this.socket_.inputStream()); + + // Get the first response + request.streamInFrom(dis); + Vector result = new Vector(); + + // Loop while we don't see a null response with the MAX_ENUM request type + while (request.requestType() != NameRequest.MAX_ENUM) { + Vector entry = new Vector(); + + // Create an element in the main vector + entry.addElement(request.name()); + entry.addElement(request.value()); + entry.addElement(request.type()); + entry.trimToSize(); + + // Add it to the result + result.addElement(entry); + + // Get another NameRequest + request.streamInFrom(dis); + } + + result.trimToSize(); + + return result; + } + + // The SOCKStream used to communication with the service + SOCKStream socket_; +}; + + + + + + + diff --git a/java/netsvcs/Naming/NameReply.java b/java/netsvcs/Naming/NameReply.java new file mode 100644 index 00000000000..52ebb111574 --- /dev/null +++ b/java/netsvcs/Naming/NameReply.java @@ -0,0 +1,145 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Naming + * + * = FILENAME + * NameReply.java + * + * Used by the naming server to give quick status messages + * to the client. This is only used to signal the success or + * failure of bind and unbind requests. The error number is + * unused (same in C++ version?). + * + *@see netsvcs.Naming.NameHandler + * + *@author Everett Anderson + * + *************************************************/ +package netsvcs.Naming; + +import java.io.*; +import java.util.*; +import JACE.OS.*; +import JACE.Connection.*; +import JACE.Reactor.*; +import JACE.ASX.*; +import JACE.SOCK_SAP.*; + +public class NameReply +{ + // Success and failure constants + public final static int SUCCESS = 1; + public final static int FAILURE = 2; + + /** + * Default Constructor + */ + public NameReply () + { + this.type_ = this.SUCCESS; + this.errno_ = 0; + } + + /** + * Constructor + * + *@param type Success or failure + *@param err Error number (unused) + */ + public NameReply (int type, int err) + { + this.type_ = type; + this.errno_ = err; + } + + /** + * Length accessor + */ + int length() + { return this.length_; } + + /** + * Type accessor -- success or failure + */ + int type() + { return this.type_; } + + /** + * Error number accessor + */ + int errno() + { return this.errno_; } + + /** + * Set type + * @param type New type + */ + void type(int type) + { this.type_ = type; } + + /** + * Set error number + * @param errno New error number + */ + void errno(int errno) + { this.errno_ = errno; } + + /** + * Send this data to the given SOCKStream + * + *@param sock SOCKStream to send to + */ + public void streamOutTo (JACE.SOCK_SAP.SOCKStream sock) throws IOException + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bout); + + dos.writeInt(this.length_); + dos.writeInt(this.type_); + dos.writeInt(this.errno_); + + dos.flush(); + + byte[] array = bout.toByteArray(); + + sock.sendN(array, 0, array.length); + } + + /** + * Fill the fields of this instance from data in the socket + * + *@param sock SOCKStream to read from + */ + public void streamInFrom (JACE.SOCK_SAP.SOCKStream sock) throws IOException + { + DataInputStream dis = new DataInputStream(sock.inputStream()); + + this.streamInFrom(dis); + } + + /** + * Send this data to the given DataInputStream (which should be buffered) + * + *@param dis DataInputStream to use + */ + public void streamInFrom (DataInputStream dis) throws IOException + { + int length = dis.readInt(); + + if (length != this.length_) + throw new IOException("Incorrect NameReply length"); + + type_ = dis.readInt(); + errno_ = dis.readInt(); + } + + final static int length_ = 12; + + int type_; + int errno_; +} + + + + diff --git a/java/netsvcs/Naming/NameRequest.java b/java/netsvcs/Naming/NameRequest.java new file mode 100644 index 00000000000..f8a3579fa35 --- /dev/null +++ b/java/netsvcs/Naming/NameRequest.java @@ -0,0 +1,331 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Naming + * + * = FILENAME + * NameRequest.java + * + * Used by both client and naming server as detailed in + * the NameHandler. This structure transfers information + * including name, value, type, and request type. + * + *@see netsvcs.Naming.NameHandler + * + *@author Everett Anderson + * + *************************************************/ +package netsvcs.Naming; + +import java.io.*; +import java.util.*; +import JACE.OS.*; +import JACE.Connection.*; +import JACE.Reactor.*; +import JACE.ASX.*; +import JACE.SOCK_SAP.*; + +public class NameRequest +{ + /** + * Different types of requests + */ + public static final int BIND = 1; + public static final int REBIND = 2; + public static final int RESOLVE = 3; + public static final int UNBIND = 4; + public static final int LIST_NAMES = 5; + public static final int LIST_VALUES = 13; + public static final int LIST_TYPES = 21; + public static final int LIST_NAME_ENTRIES = 6; + public static final int LIST_VALUE_ENTRIES = 14; + public static final int LIST_TYPE_ENTRIES = 22; + public static final int MAX_ENUM = 11; + + /** + * Default constructor + */ + public NameRequest () + { + this.name_ = this.value_ = this.type_ = null; + this.length_ = 32; + } + + /** + * Constructor + * + * @param requestType Type of request this is (BIND, REBIND, etc) + * @param name Key to bind + * @param value Value to bind + * @param type Type to couple with the value + * @param timeout Timer information (not really used in JACE yet) + */ + public NameRequest(int requestType, + String name, + String value, + String type, + TimeValue timeout) + { + this.requestType_ = requestType; + + if (timeout == null) { + + this.blockForever_ = 1; + this.secTimeout_ = 0; + this.usecTimeout_ = 0; + } else { + + this.blockForever_ = 0; + this.secTimeout_ = (int)timeout.sec(); + this.usecTimeout_ = (int)timeout.getMilliTime() * 1000; + } + + // This is necessary to make sure null pointer exceptions are + // avoided. It makes it more consistent later on + if (name == null) + this.name_ = new String(""); + else + this.name_ = new String(name); + if (value == null) + this.value_ = new String(""); + else + this.value_ = new String(value); + if (type == null) + this.type_ = new String(""); + else + this.type_ = new String(type); + + // Set the length + this.calculateLength(); + } + + /** + * Calculate the transmission length (bytes) of this structure + */ + private void calculateLength() + { + // The type is sent as an 8 bit data type (chars in the C++ version), + // but the name and value are sent as 16 bit chars (ACE_USHORT16's in C++) + + this.length_ = 34 + this.type_.length() + 2 * (this.name_.length() + + this.value_.length()); + + } + + /** + * Return the transmission length + */ + public int length() + { return this.length_; } + + /** + * Return the name/key + */ + public String name() + { return new String(this.name_); } + + /** + * Set the name/key + * @param name Name to set to + */ + public void name(String name) + { + if (name == null) + this.name_ = new String(""); + else + this.name_ = new String(name); + + this.calculateLength(); + } + + /** + * Return the value + */ + public String value() + { return new String(this.value_); } + + /** + * Set the value + * @param value New value + */ + public void value(String value) + { + if (value == null) + this.value_ = new String(""); + else + this.value_ = new String(value); + + this.calculateLength(); + } + + /** + * Return the type + */ + public String type() + { return new String(this.type_); } + + /** + * Set the type + * @param type New type + */ + public void type(String type) + { + if (type == null) + this.type_ = new String(""); + else + this.type_ = new String(type); + + this.calculateLength(); + } + + /** + * Fill the fields of this instance with data from the socket + * + *@param sock Socket to read from + */ + public void streamInFrom (JACE.SOCK_SAP.SOCKStream sock) throws IOException + { + DataInputStream dis = new DataInputStream(sock.inputStream()); + + this.streamInFrom(dis); + } + + /** + * Fill the fields of this instance from the given DataInputStream + * + *@param dis DataInputStream to read from + */ + public void streamInFrom (DataInputStream dis) throws IOException + { + // Read the length (32 bits) + length_ = dis.readInt(); + + // Read the request type + requestType_ = dis.readInt(); + + // Can we block forever to fulfill this request? (unused) + blockForever_ = dis.readInt(); + + // How long until we should time out this request? (unused) + secTimeout_ = dis.readInt(); + usecTimeout_ = dis.readInt(); + + // The sizes are in bytes, and there are two bytes per char + // (ACE_USHORT16 in C++ land) + int nameLen = dis.readInt() / 2; + int valueLen = dis.readInt() / 2; + + int typeLen = dis.readInt(); + + // Read the name -- just read chars since they're 16 bits. + // Hopefully the SOCKStream has buffered the data + char buf[] = new char[nameLen]; + for (int i = 0; i < nameLen; i++) { + buf[i] = dis.readChar(); + } + this.name_ = new String(buf); + + // Read the value + buf = new char[valueLen]; + for (int i = 0; i < valueLen; i++) + buf[i] = dis.readChar(); + this.value_ = new String(buf); + + // Read the type -- now we can use readFully since + // the type was sent as 8 bit chars + byte tbuf[] = new byte[typeLen]; + dis.readFully(tbuf); + this.type_ = new String(tbuf); + + // Skip the null char at the end + dis.skipBytes(2); + } + + /** + * Send this NameRequest out to the given SOCKStream + * + *@param sock SOCKStream to send to + */ + public void streamOutTo (JACE.SOCK_SAP.SOCKStream sock) throws IOException + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bout); + + dos.writeInt(length_); + dos.writeInt(requestType_); + dos.writeInt(blockForever_); + dos.writeInt(secTimeout_); + dos.writeInt(usecTimeout_); + + // Byte sizes are sent, and the name and value are stored as + // 16 bit char arrays (ACE_USHORT16 arrays in C++ version) + dos.writeInt(this.name_.length() * 2); + dos.writeInt(this.value_.length() * 2); + dos.writeInt(this.type_.length()); + + // Making sure the name_ wasn't null comes in handy + // in situations like this + dos.writeChars(this.name_); + dos.writeChars(this.value_); + dos.writeBytes(this.type_); + + // Null termination + dos.writeChar(0); + + // Send it for real + dos.flush(); + + byte[] array = bout.toByteArray(); + + sock.sendN(array, 0, array.length); + } + + /** + * Set the requestType + *@param type Type to set to + */ + public void requestType(int type) + { + this.requestType_ = type; + } + + /** + * Get requestType + */ + public int requestType() + { + return this.requestType_; + } + + /** + * Can we block forever to fulfill the request? (unused) + */ + public boolean blockForever() + { + return (this.blockForever_ != 0) ? true : false; + } + + /** + * Allowed timeout (unused) + */ + public int secTimeout() + { + return this.secTimeout_; + } + + int length_; + int requestType_; + int blockForever_; + int secTimeout_; + int usecTimeout_; + + String name_; + String value_; + String type_; +}; + + + + + + |