SpaceKit Documentation Series F. Morton Invisible Worlds, Inc. May 1, 2000 Blocks SpaceKit for Java Abstract The Blocks SpaceKit for Java is a software development kit that implements the Blocks eXtensible eXchange Protocol (BXXP) in Java [1]. The SpaceKit provides tools to access existing Blocks servers as well as providing a framework for developing Blocks servers using Java. To subscribe to the Blocks discussion list, send e-mail[3]; there is also a developers' site[4]. Morton [Page 1] Technical Memo Java SpaceKit May 2000 Table of Contents 1. What Do I Need To Use The Blocks SpaceKit for Java? . . . 4 1.1 Additional Documentation . . . . . . . . . . . . . . . . . 4 2. The Blocks SpaceKit for Java Client . . . . . . . . . . . 4 2.1 A Complete SpaceExample For The Impatient . . . . . . . . 4 2.2 Connecting . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.1 Connecting With Localization . . . . . . . . . . . . . . . 9 2.3 Channel Creation . . . . . . . . . . . . . . . . . . . . . 9 2.3.1 Non-Authenticated Channels . . . . . . . . . . . . . . . . 9 2.3.2 SASL Authenticated Channels . . . . . . . . . . . . . . . 9 2.3.2.1 SASL Anonymous Authenticated Channels . . . . . . . . . . 9 2.3.2.2 SASL OTP (One-Time Password) Authenticated Channels . . . 10 2.4 Requests . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.5 XML Parsers . . . . . . . . . . . . . . . . . . . . . . . 10 2.6 Responses and Flow Control . . . . . . . . . . . . . . . . 11 2.7 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . 11 2.8 Tracing . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.9 Client Examples . . . . . . . . . . . . . . . . . . . . . 12 2.9.1 SpaceAuthenticate . . . . . . . . . . . . . . . . . . . . 12 2.9.2 SpaceBig . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.9.3 SpacePing . . . . . . . . . . . . . . . . . . . . . . . . 13 2.9.4 SpaceTest . . . . . . . . . . . . . . . . . . . . . . . . 13 2.9.5 SpaceTest2 . . . . . . . . . . . . . . . . . . . . . . . . 13 3. The Blocks SpaceKit for Java Server: SpaceBxxd . . . . . . 14 3.1 Configuration . . . . . . . . . . . . . . . . . . . . . . 14 3.1.1 Configuration File Format . . . . . . . . . . . . . . . . 14 3.1.2 Running SpaceBxxd from the Command Line . . . . . . . . . 15 3.1.3 Running SpaceBxxd Under inetd . . . . . . . . . . . . . . 15 3.1.4 User Properties File Format . . . . . . . . . . . . . . . 15 3.2 A Complete Module Example For The Impatient . . . . . . . 16 3.2.1 Profile Definition . . . . . . . . . . . . . . . . . . . . 16 3.2.2 Server-side SpaceModuleREVERSE . . . . . . . . . . . . . . 16 3.2.3 Client-side SpaceReverse . . . . . . . . . . . . . . . . . 18 3.3 Module Structure . . . . . . . . . . . . . . . . . . . . . 19 3.4 Responses . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4.1 The start Response . . . . . . . . . . . . . . . . . . . . 20 3.4.2 The response Response . . . . . . . . . . . . . . . . . . 20 3.4.3 The error Response . . . . . . . . . . . . . . . . . . . . 21 3.4.4 The fatal Response . . . . . . . . . . . . . . . . . . . . 21 3.5 Logging . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.6 Users . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.7 Saving State . . . . . . . . . . . . . . . . . . . . . . . 23 4. Localization Class Structure . . . . . . . . . . . . . . . 24 4.1 A Localization Class Example . . . . . . . . . . . . . . . 24 5. Utilities . . . . . . . . . . . . . . . . . . . . . . . . 25 5.1 SpaceKeyOtp: OTP Key Generation . . . . . . . . . . . . . 25 6. Reference . . . . . . . . . . . . . . . . . . . . . . . . 25 6.1 SpaceAuthenticatorSasl . . . . . . . . . . . . . . . . . . 25 Morton [Page 2] Technical Memo Java SpaceKit May 2000 6.2 SpaceChannel . . . . . . . . . . . . . . . . . . . . . . . 26 6.3 SpaceConfig . . . . . . . . . . . . . . . . . . . . . . . 27 6.4 SpaceException . . . . . . . . . . . . . . . . . . . . . . 27 6.5 SpaceLog . . . . . . . . . . . . . . . . . . . . . . . . . 28 6.6 SpaceMessage . . . . . . . . . . . . . . . . . . . . . . . 28 6.7 SpaceModuleThread . . . . . . . . . . . . . . . . . . . . 29 6.8 SpacePacket . . . . . . . . . . . . . . . . . . . . . . . 30 6.9 SpaceProfile . . . . . . . . . . . . . . . . . . . . . . . 32 6.10 SpaceResponse extends SpacePacket . . . . . . . . . . . . 32 6.11 SpaceRequest extends SpacePacket . . . . . . . . . . . . . 33 6.12 SpaceSep . . . . . . . . . . . . . . . . . . . . . . . . . 33 6.13 SpaceServer . . . . . . . . . . . . . . . . . . . . . . . 33 6.14 SpaceSocket . . . . . . . . . . . . . . . . . . . . . . . 35 6.15 SpaceTimeout . . . . . . . . . . . . . . . . . . . . . . . 36 6.16 SpaceUserCache . . . . . . . . . . . . . . . . . . . . . . 36 References . . . . . . . . . . . . . . . . . . . . . . . . 36 Author's Address . . . . . . . . . . . . . . . . . . . . . 37 A. Blocks Public License . . . . . . . . . . . . . . . . . . 37 Morton [Page 3] Technical Memo Java SpaceKit May 2000 1. What Do I Need To Use The Blocks SpaceKit for Java? The Blocks SpaceKit for Java requires "spacekit.jar" to be in the $CLASSPATH as required by the Java JDK. Optionally, it can be included in the java command line using -classpath as in this example to ping the indicated blocks server: java -classpath ./spacekit.jar SpacePing sqa.invisible.net Java version 1.2.1 or later is required. 1.1 Additional Documentation o The Blocks eXtensible eXchange Protocol[5][1] o The Blocks Simple Exchange Profile[6][2] 2. The Blocks SpaceKit for Java Client 2.1 A Complete SpaceExample For The Impatient The SpaceExample shows a typical flow for creating a connection to a SpaceServer, creating a SpaceChannel and doing a simple SEP request: public class SpaceExample { //------------------------------------------------------------------------------ // main //------------------------------------------------------------------------------ public static void main(String[] argv) { SpaceServer server = null; try { //-------------------------------------------------------------------- // set blocks server host //-------------------------------------------------------------------- if(argv.length != 1) { System.err.println("Usage: java SpaceExample hostname"); return; } //-------------------------------------------------------------------- // instantiate the blocks server //-------------------------------------------------------------------- server = new SpaceServer(argv[0]); server.setTrace(true); //-------------------------------------------------------------------- // connect to blocks server //-------------------------------------------------------------------- Morton [Page 4] Technical Memo Java SpaceKit May 2000 SpaceResponse greeting = server.connect(true); //-------------------------------------------------------------------- // show valid profiles //-------------------------------------------------------------------- String[] profiles = greeting.getProfiles(); for(int k=0;k\r\n"); fetch.append("\r\n"); fetch.append("\r\n"); fetch.append("\r\n"); fetch.append("\r\n"); fetch.append("mrose@\r\n"); fetch.append("\r\n"); fetch.append("\r\n"); fetch.append("\r\n"); fetch.append("\r\n"); //-------------------------------------------------------------------- Morton [Page 5] Technical Memo Java SpaceKit May 2000 // wait on the channel start and see if error //-------------------------------------------------------------------- channel.wait(response); if(response.isError()) { System.err.println("Start Error: " + response.getErrorCode()); return; } //-------------------------------------------------------------------- // do the request //-------------------------------------------------------------------- response = channel.request(fetch.toString(),true); if(response.isError()) { System.err.println("Request Error: " + response.getErrorCode()); return; } //-------------------------------------------------------------------- // show the response on standard output //-------------------------------------------------------------------- System.out.println(response.toString()); //-------------------------------------------------------------------- // free the response //-------------------------------------------------------------------- channel.free(response); } catch(SpaceTimeout e) { System.err.println(e.toString()); } catch(SpaceException e) { System.err.println(e.toString()); } finally { //-------------------------------------------------------------------- // always close the server //-------------------------------------------------------------------- if(server != null) { server.close(); } } } } Sample Output From SpaceExample: ------------------------------------------------------------------ Morton [Page 6] Technical Memo Java SpaceKit May 2000 : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 0 : 0 : 255 : 1 : Yes : No : ------------------------------------------------------------------ ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 0 : 0 : 255 : 1 : Yes : No : ------------------------------------------------------------------ Profile: http://xml.resource.org/profiles/sasl/ANONYMOUS Profile: http://xml.resource.org/profiles/SEP Profile: http://xml.resource.org/profiles/NULL/ECHO Profile: http://xml.resource.org/profiles/NULL/SINK Profile: http://xml.resource.org/profiles/sasl/OTP Localize: en-US Features: none ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 0 : 1 : 51 : 1 : Yes : No : ------------------------------------------------------------------ ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 0 : 1 : 51 : 1 : Yes : No : ------------------------------------------------------------------ ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 1 : 1 : 1335 : 1 : Yes : No : ------------------------------------------------------------------ ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 1 : 1 : 1335 : 1 : Yes : No : ------------------------------------------------------------------ Writing I-Ds and RFCs using XML Invisible Worlds, Inc.
660 York Street Morton [Page 7] Technical Memo Java SpaceKit May 2000 San Francisco CA 94110 US +1 415 695 3975 mrose@invisible.net http://invisible.net/
General RFC Request for Comments I-D Internet-Draft XML Extensible Markup Language
------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 0 : 1 : 0 : 1 : Yes : No : ------------------------------------------------------------------ 2.2 Connecting A single instance of the SpaceServer class exists for each socket connection to the space server. To open a connection to the server sqa.invisible.net on the default port (port 10288): SpaceServer server = new SpaceServer("sqa.invisible.net"); SpaceResponse greeting = server.connect(); The connect() waits for the connection to complete, returning the greeting. To get a list of available profiles, use the getProfiles() method: String[] profiles = greeting.getProfiles(); for(int k=0;k\r\n"); sep.append("\r\n"); sep.append("\r\n"); sep.append(""); sep.append("\r\n"); sep.append("IN\r\n"); sep.append("\r\n"); sep.append("\r\n"); sep.append("\r\n"); sep.append("\r\n"); SpaceResponse response = channel.request(sep.toString(),false); //...can do other things here while fetch is processing channel.wait(response); //...use the response results using response.toString() channel.free(response); See "Responses and Flow Control" for an explanation of channel.wait(). 2.5 XML Parsers The response contains XML that may act as input to any XML parser. For example, the following creates a DOM document using the Apache-Xerces (xml.apache.org) parser: Morton [Page 10] Technical Memo Java SpaceKit May 2000 InputStream in = response.getInputStream(); DOMParser parser = new DOMParser(); parser.parse(new InputSource(in)); DocumentImpl document = (DocumentImpl)parser.getDocument(); 2.6 Responses and Flow Control A SpaceResponse returns from many of the SpaceServer methods, such as connect(), start() and request(). All of these methods take an optional boolean "wait" argument. If set to true, the returned response is guaranteed to be "complete" unless an error has occurred. You can check to see if the response is an error using the "isError()" method. If "isError()" returns true, the methods "getErrorCode()" and "getErrorText()" returns the int error code and String error text, respectively. If the optional "wait" argument is specified to be false, the requesting method returns immediately with an "incomplete" response while a separate thread continues to receive the response. Any number of incomplete requests can exist concurrently. At any time, whether a response is "complete" or "incomplete," you can check the state of the response using methods such as "isIncomplete()" and "getLength()." See the reference section for a complete list. To wait for the response to complete, use the wait method from the SpaceServer: channel.wait(response); The wait() can be specified with an optional timeout. When wait() returns it is guaranteed to be complete unless an error occurs. After the wait is complete, the response is removed from the response queue, but the content of the response is preserved, accessible with either "getString()" or "getInputStream()." To mark the content as no longer needed, close the response using: channel.free(response); 2.7 Exceptions Two exceptions are described in the Reference section: SpaceException and SpaceTimeout. 2.8 Tracing Tracing is a very helpful debugging tool to view the response queue set by doing: Morton [Page 11] Technical Memo Java SpaceKit May 2000 server.setTrace(true); Since responses are often broken up into multiple frames, during tracing every time a frame arrives, the status of the entire response queue is shown to System.err, looking something like: ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 7 : 23 : 0 : 0 : No : No : : 1 : 7 : 22 : 0 : 0 : No : No : : 2 : 7 : 21 : 0 : 0 : No : No : : 3 : 7 : 20 : 0 : 0 : No : No : : 4 : 7 : 19 : 0 : 0 : No : No : : 5 : 7 : 18 : 0 : 0 : No : No : : 6 : 7 : 17 : 0 : 0 : No : No : : 7 : 7 : 16 : 0 : 0 : No : No : : 8 : 7 : 15 : 0 : 0 : No : No : : 9 : 5 : 9 : 0 : 0 : No : No : : 10 : 7 : 14 : 0 : 0 : No : No : : 11 : 3 : 8 : 27654 : 1 : Yes : No : : 12 : 13 : 13 : 0 : 0 : No : No : : 13 : 1 : 7 : 27654 : 4 : Yes : No : : 14 : 11 : 12 : 0 : 0 : No : No : : 15 : 0 : 6 : 51 : 1 : Yes : No : : 16 : 9 : 11 : 0 : 0 : No : No : : 17 : 0 : 5 : 51 : 1 : Yes : No : ------------------------------------------------------------------ Where: ID=response queue number Channel=channel number associated with the response Serial=serial number associated with the response Length=the number of bytes received so far Frames=the number of frames received so far Complete=is the response complete (Yes or No) Error=has an error occurred (Yes or No) 2.9 Client Examples 2.9.1 SpaceAuthenticate Description: Opens a channel with the SpaceProfile.saslAnonymous profile and the SpaceProfile.saslOtp profile. Command Line Example: java SpaceAuthenticate sqa.invisible.net blockmaster secretpassword Morton [Page 12] Technical Memo Java SpaceKit May 2000 2.9.2 SpaceBig Description: Implements the Blocks Information Groper. Command Line Example (returns list of valid "name" attributes): java SpaceBig -server sqa.invisible.net search "..." Command Line Example (fetch the contents of the specified document to System.out): java SpaceBig -server sqa.invisible.net fetch doc.rfc.2629 Command Line Example (store the block using content from System.in): java SpaceBig -server sqa.invisible.net -username yourusername -passphrase yourpassphrase store test.yoursubtree test.yoursubtree.2629 write 1 This is a configuration file for the default port 10288. Morton [Page 14] Technical Memo Java SpaceKit May 2000 3.1.2 Running SpaceBxxd from the Command Line SpaceBxxd can run from the command line in "stand-alone" mode. Once started, it continues to run indefinitely. When a client connects, it is the exclusive connection to the server until the close of the session. This mode of running is typically used for testing purposes. When running in stand-alone mode, the "$CLASSPATH" environment variable should contain all classes required by the server, including spacekit.jar and any additional modules. This example starts the server on port 12345 using the configuration file in the current directory named bxxd.config: java SpaceBxxd -standalone -portno 12345 bxxd.config Without "-portno" the server uses the default port, 10288. 3.1.3 Running SpaceBxxd Under inetd To run the server under the supervision of inetd, first add the following to /etc/services: bxxp 10288/tcp Secondly, add the following to /etc/inetd.conf (Note: this shows as being on two lines but should actually be one line): bxxp stream tcp nowait bxxpuser /usr/java1.2/bin/java java -classpath /iw/SpaceKit/java/current/classes/spacekit.jar SpaceBxxd /usr/local/bxxd/etc/bxxd.config Where: 1. bxxpuser is the supervisory username of the server. 2. /usr/java1.2/bin/java is the path to the java runtime executable. 3. /iw/SpaceKit/java/current/classes/spacekit.jar is the full pathname to the SpaceKit spacekit.jar file. 4. /usr/local/bxxd/etc/bxxd.config is the full pathname to the server configuration file. Finally, restart inetd as super-user: kill -HUP pid Where "pid" is the process id of inetd. 3.1.4 User Properties File Format A properties file defines users of the system. The location of the user properties file is found in the configuration file. Two Morton [Page 15] Technical Memo Java SpaceKit May 2000 elements, the "homeDirectory" and the "authDirectory" combine to form the directory name containing the properties file. The filename itself is a combination of the port number with the ".profiles" suffix. For port 12345 the typical user file pathname is: /usr/local/bxxd/auth/12345.properties The user file contains any number of users. An example entry for a user file is: username.fmorton.name=Frank Morton username.fmorton.authentication.mechanism=otp username.fmorton.authentication.sequence=9911 username.fmorton.authentication.seed=rwcsqa0120696 username.fmorton.authentication.key=c055f88c0ca8c0ac username.fmorton.authentication.privacy=none username.fmorton.authentication.algorithm=sha1 To create an OTP key, see the SpaceKeyOtp utility. 3.2 A Complete Module Example For The Impatient You can create your own modules by defining a new profile and implementing a new server module as well as the client using the new profile. In the example, we create a module named "reverse" that receives a server request, reverses the order of the octets in the request, and responds back to the client with the reversed results. 3.2.1 Profile Definition To access your new module, first define the new profile and its corresponding module in the configuration file for the port using the new profile: If you are using the server in stand-alone mode, you will need to restart since the configuration file loads at start time 3.2.2 Server-side SpaceModuleREVERSE A module extends the class "SpaceModuleThead" as in the Morton [Page 16] Technical Memo Java SpaceKit May 2000 SpaceModuleREVERSE example: public class SpaceModuleREVERSE extends SpaceModuleThread { //------------------------------------------------------------------------------ // run //------------------------------------------------------------------------------ public void run() { try { //--------------------------------------------------------------- // see if channel start //--------------------------------------------------------------- if(isStart()) { start(""); return; } //-------------------------------------------------------------------- // normal processing for the module //-------------------------------------------------------------------- String reverse = (new StringBuffer(request.toString())).reverse().toString(); //-------------------------------------------------------------------- // return a response //-------------------------------------------------------------------- respond(reverse); //-------------------------------------------------------------------- // log the reverse response //-------------------------------------------------------------------- log("reverse request complete"); } catch(SpaceTimeout e) { //--------------------------------------------------------------- // timeout //--------------------------------------------------------------- fatal(e); } catch(SpaceException e) { //--------------------------------------------------------------- // exception //--------------------------------------------------------------- fatal(e); } } } A module has two main jobs: 1. Start the channel. Morton [Page 17] Technical Memo Java SpaceKit May 2000 2. Process requests. See "Module Structure" below and the reference section for a complete description of the SpaceModuleThread class. 3.2.3 Client-side SpaceReverse The SpaceReverse application is an example of how to access the "reverse" module: public class SpaceReverse { //------------------------------------------------------------------------------ // main //------------------------------------------------------------------------------ public static void main(String[] argv) { SpaceServer server = null; try { //-------------------------------------------------------------------- // parse the command line //-------------------------------------------------------------------- String host = null; int port = SpaceServer.defaultPort; for(int k=0;ktseuqer/'1'=onqer tseuqer< 3.3 Module Structure All modules extend the class "SpaceModuleThread." Since SpaceBxxp is a multi-threaded server, all modules must be Morton [Page 19] Technical Memo Java SpaceKit May 2000 thread safe. Module names specified in the configuration file, such as "sasl-otp," convert to a class name made up of "SpaceModule" and the "module" capitalized with all hyphens removed. So, for the module "sasl-otp," "SpaceModuleSASLOTP" is the final class name. This is necessary to avoid class naming problems. The following protected instance variables are defined in SpaceModuleThread and are fully accessible by modules extending the class: o SpaceConfig config o SpaceUserCache userCache o SpaceProfile profile o SpaceServer server o SpaceRequest request See the reference for a full definition of each class. To access the content of the request, a module uses "request.toString()." 3.4 Responses Four types of responses are available to client requests: 1. start 2. response 3. error 4. fatal 3.4.1 The start Response When any module receives a request, the module must first determine whether it is a start channel request. If it is, it should respond using the start method with content as defined by the module API. An empty response is commonly used to acknowledge the start of a channel: if(isStart()) { start(""); return; } 3.4.2 The response Response A response() is the normal method to respond to a request. String results = yourResultsMethod(); respond(results); Morton [Page 20] Technical Memo Java SpaceKit May 2000 3.4.3 The error Response Use the error() method to return error responses. To return a standard error response, such as error code 550: error(550); To return your own error code and message use: error(1000,"Your Error Message"); See SpaceMessage in the reference section for a list of standard error messages. The error() methods log the errors as described in Logging below. 3.4.4 The fatal Response In the event that a SpaceException or SpaceTimeout is thrown in your module, at a minimum the fatal() method should be called when catching either exception: catch(SpaceTimeout e) { //-------------------------------------------------------------------- // timeout //-------------------------------------------------------------------- fatal(e); } catch(SpaceException e) { //-------------------------------------------------------------------- // exception //-------------------------------------------------------------------- fatal(e); } The fatal() method logs the exception as described in Logging below. The fatal() method may be called any time in your catch clause. It does not have to be the first or last call in the clause. 3.5 Logging The log() methods cause a message to be appended to the log file specified for the port in the configuration file. The first form of the log method implies a "SpaceLog.info" message level: log("your log message"); Morton [Page 21] Technical Memo Java SpaceKit May 2000 The second form of the log method takes a message level and message as arguments: log(SpaceLog.notify,"your notify message"); See SpaceLog in the reference section for a complete list of message levels. The log contains the following items on each line: o Date in the format mm/dd. o Time in the form hh:mm:ss. o Port number. o If appropriate, the channel number on the port. o If appropriate, the serial number of the request. o The message text. Sample log messages from SpaceModuleREVERSE: 03/18 12:01:16 info 12345 listen 03/18 12:01:26 info 12345 connect 03/18 12:01:27 info 12345.0.1 module Reverse 03/18 12:01:27 info 12345.0.1 start 03/18 12:01:27 info 12345.1.1 module Reverse 03/18 12:01:27 info 12345.1.1 start 03/18 12:01:27 info 12345.1.1 reverse request complete 03/18 12:01:27 info 12345 disconnect The "module Reverse" message shows two times because the first request on channel 0 is the start channel request. The second message is from the request itself on channel 1. Since serial numbers are reused, both requests used serial number 1 in this example. 3.6 Users The user definition file for a particular port can be loaded using the loadUser() method. This loads all users for the port into a SpaceUserCache(). Note that subsequent calls to the method recognize that the cache is already loaded and therefore it will not be reloaded unnecessarily. The method getUser() gets a specific element from the cache. For example, if the user file contains this entry for the user fmorton: username.fmorton.authentication.sequence=9911 To get that value, pass the element class and name to the method: String sequence = getUser("fmorton","authentication","sequence"); Morton [Page 22] Technical Memo Java SpaceKit May 2000 Define any additional classes and elements by inserting the text into the user definition file. For example, if the SpaceModuleREVERSE wanted to optionally capitalize the reversed response for certain users, add an element: username.fmorton.reverseattribute.capitalize=yes Then to get the value of the capitalize element in the module: String capitalize = getUser("fmorton","reverseattribute","capitalize"); The user definition file can contain any number of elements in any order. 3.7 Saving State Since multiple requests can be made on a channel, saving results from a previous request is often necessary. Every channel has an optional list of parameters that may be set. The parameters are key-value pairs. Parameters may be set on channel zero, the start channel and the current channel using: 1. setOnChannelZero() 2. setOnStartChannel() 3. set() As an example, the SpaceModuleSASLOTP saves the authenticator as a channel parameter during channel start in both the start channel and channel zero: setOnChannelZero("authenticator",authenticator); setOnStartChannel("authenticator",authenticator); Where the variable "authenticator" contains, as an example, the value "fmorton." To set a parameter on the current channel: set("thingtoremenber",thingtoremember); To retrieve a value, use the following access methods: 1. getFromChannelZero() 2. getFromStartChannel() 3. get() To retrieve the authenticator from channel zero set by SpaceModuleSASLOTP: Morton [Page 23] Technical Memo Java SpaceKit May 2000 String authenticator = getFromChannelZero("authenticator"); Any number of parameters by any name may be set. 4. Localization Class Structure Localization support can be added to the SpaceKit by creating a message class for the particular language to support. The class name is "SpaceMessage" followed by the language mnemonic with hyphens removed and capitalized. For example, the class name for "en-us" is "SpaceMessageENUS" and is an implemention of the "SpaceMessage" interface. The class contains one method get() with a single int argument specifying the message number. Error codes are considered message numbers. The class must be defined in the $CLASSPATH for the Java virtual machine. 4.1 A Localization Class Example This is the implementation of the "en-us" language as distributed with the Blocks SpaceKit for Java: Morton [Page 24] Technical Memo Java SpaceKit May 2000 public class SpaceMessageENUS implements SpaceMessage { //------------------------------------------------------------------------------ // lookup message //------------------------------------------------------------------------------ public String get(int messageCode) { if(messageCode == 421) return("service not available"); if(messageCode == 450) return("requested action not taken"); if(messageCode == 451) return("requested action aborted"); if(messageCode == 454) return("temporary authentication failure"); if(messageCode == 500) return("general syntax error"); if(messageCode == 501) return("missing NUL separator"); if(messageCode == 504) return("parameter not implemented"); if(messageCode == 530) return("authentication required"); if(messageCode == 534) return("authentication mechanism insufficient"); if(messageCode == 535) return("authentication failure"); if(messageCode == 537) return("action not authorized for user"); if(messageCode == 538) return("authentication mechanism requires encryption"); if(messageCode == 550) return("requested action not taken"); if(messageCode == 553) return("parameter invalid"); if(messageCode == 554) return("transaction failed"); return(null); } } 5. Utilities 5.1 SpaceKeyOtp: OTP Key Generation Each user defined in a user properties file contains an authorization key associated with the authorization mechanism. The SpaceKeyOtp utility arguments are algorithm, passphrase (not included in the user properties file), seed and count: java SpaceKeyOtp sha1 secretpassphrase rwcsqa0120696 9911 The key is sent to standard output: c055f88c0ca8c0ac 6. Reference 6.1 SpaceAuthenticatorSasl constructor SpaceAuthenticatorSasl(String profile) Valid profiles include: http://xml.resource.org/profiles/sasl/ANONYMOUS http://xml.resource.org/profiles/SEP Morton [Page 25] Technical Memo Java SpaceKit May 2000 http://xml.resource.org/profiles/NULL/ECHO http://xml.resource.org/profiles/NULL/SINK http://xml.resource.org/profiles/sasl/OTP String getProfile() Get the profile associated with the authenticator. String getRequest() Get the SEP request used to create the channel. String getResponse(SpaceResponse response) Get the necessary response to the response returned from getRequest(). String set(String key,String value) Set a key-value pair used by getRequest() and getResponse(). 6.2 SpaceChannel void free(SpaceResponse response) Free a response. int getChannel() Get the channel number. boolean getError() Get if channel error. int getErrorCode() Get the most recent error code. String getErrorCodeString() Get the most recent error code string. String getErrorString() Get the most recent error description. String getMime() Get the MIME type of the last response on the channel. String getProfile() Get the channel profile. long getSeqno() Get the channel seqno. boolean getStarted() Get if the channel has been started. Morton [Page 26] Technical Memo Java SpaceKit May 2000 boolean isError() Get if an error has occurred on the channel. boolean isNotStarted() Get if the channel has not been started. boolean isStarted() Get if the channel has been started. SpaceResponse request(String request) throws SpaceTimeout,SpaceException SpaceResponse request(String request,boolean wait) throws SpaceTimeout,SpaceException Send a request with optional wait. SpaceResponse respond(String request) throws SpaceTimeout,SpaceException SpaceResponse respond(String request,boolean wait) throws SpaceTimeout,SpaceException Send a response with optional wait. A "respond" is the same as a "request" without the "" tags. It is used to respond to a SASL OTP challenge. 6.3 SpaceConfig constructor SpaceConfig() String get(String key) Get the configuration parameter using the supplied key. String getGreeting() Get the greeting response associated with the configuration. SpaceProfile getProfile(String uri) Get the profile associated with the supplied uri. void set(String key,String value) Set the configuration parameter using the supplied key-value pair. 6.4 SpaceException constructor SpaceException() constructor SpaceException(String message) Morton [Page 27] Technical Memo Java SpaceKit May 2000 6.5 SpaceLog static int error = 0 static int debug1 = 1 static int debug2 = 2 static int debug3 = 3 static int debug4 = 4 static int notify = 5 static int fatal = 6 static int info = 7 static int stats = 8 static int system = 9 static int user = 10 constructor SpaceLog() constructor SpaceLog(SpaceConfig config,int port) void println(String message) Append a message to the log. void println(int level,String message) Append a message to the log. void println(int level,String message,SpacePacket packet) Append a message to the log. Note that a SpacePacket is the super-class of both SpaceRequest and SpaceResponse. 6.6 SpaceMessage static String get(int errorCode) Returns the textual version of the specified error code. Known codes are: 421 = "service not available" 450 = "requested action not taken" 451 = "requested action aborted" 454 = "temporary authentication failure" 500 = "general syntax error" 501 = "syntax error in parameters" Morton [Page 28] Technical Memo Java SpaceKit May 2000 504 = "parameter not implemented" 530 = "authentication required" 534 = "authentication mechanism insufficient" 535 = "authentication failure" 537 = "action not authorized for user" 538 = "authentication mechanism requires encryption" 550 = "requested action not taken" 553 = "parameter invalid" 554 = "transaction failed" 6.7 SpaceModuleThread void error(int errorCode) Respond with an error and a standard error code. void error(int errorCode,String errorString) Respond with an error and a supplied error code and error string. void fatal(Exception e) Respond with a fatal error in a catch clause. String get(String key) Get a channel parameter value. SpaceChannel getChannel() Get the requesting channel. String getFromChannelZero(String key) Get a channel parameter value from channel zero. String getFromStartChannel(String key) Get a channel parameter value from the start channel. int getSerial() Get the requesting serial number. SpaceConfig getConfig() Get the module configuration. String getUser(String authenticator,String name) Get the specified user property value. SpaceUserCache getUserCache() Get the module user cache. boolean isStart() See if the request is a start channel request. void loadUser() Morton [Page 29] Technical Memo Java SpaceKit May 2000 Load the user properties for the port if not already loaded. void log(String message) Log a message with the SpaceLog.info level. void log(int level,String message) Log a message with the specified level. void respond(String payload) Respond with a normal supplied response. void run() The thread run() method. Overridden by the module. void set(String key,String value) Set a channel parameter value. void setOnChannelZero(String key,String value) Set a channel parameter value on channel zero. void setOnStartChannel(String key,String value) Set a channel parameter value on the start channel void start(String payload) Respond to a start channel request. 6.8 SpacePacket constructor SpacePacket(SpaceChannel channel,int serial) void free() Free the packet by marking the content null. String getChallengeString() throws SpaceException Get the challenge text. First check if isChallenge() == true. int getChannel() Get the channel number from the channel associated with the packet. boolean getError() Get if an error has occurred on the channel associated with the packet. int getErrorCode() Get the most recent error code from the channel associated with the packet. String getErrorCodeString() Get the most recent error code string from the channel associated Morton [Page 30] Technical Memo Java SpaceKit May 2000 with the packet. String getErrorString() Get the most recent error description from the channel associated with the packet. String getFeatures() Get the features associated with the connection int getFrameCount() Get the current number of frames making up the packet. InputStream getInputStream() Get the input stream of the current content. Used as input for XML parsers. String getLocalize() Get the localization associated with the connection String getMime() Get the MIME type of the last packet on the channel associated with the packet. String getNames() Get the names from the root level of each block in the packet (must parse() first). String getProfile() Get the channel profile from the channel associated with the packet. String[] getProfiles() Get the profiles associated with the connection long getSeqno() Get the channel seqno from the channel associated with the packet. int getSerial() Get the serial associated with the packet. boolean isChallenge() throws SpaceException Is this response an authentication challenge? boolean isComplete() Is this packet complete? boolean isError() Has an error occurred or been found by parsing the response? boolean isIncomplete() Morton [Page 31] Technical Memo Java SpaceKit May 2000 Is this response incomplete? int length() Returns the length in bytes of the current packet content. void parse() throws SpaceException Parse the current response content for errors and challenges. String toString() Convert the entire packet content to a String. 6.9 SpaceProfile static String sep = "http://xml.resource.org/profiles/SEP" static String tls = "http://xml.resource.org/profiles/TLS" static String saslAnonymous = "http://xml.resource.org/profiles/sasl/ANONYMOUS" static String saslOtp = "http://xml.resource.org/profiles/sasl/OTP" static String saslExternal = "http://xml.resource.org/profiles/sasl/EXTERNAL" static String nullEcho = "http://xml.resource.org/profiles/NULL/ECHO" static String nullSink = "http://xml.resource.org/profiles/NULL/SINK" constructor SpaceProfile(String uriArg) void set(String key,String value) Set a profile parameter with the supplied key-value pair. String get(String key) Get a profile parameter value with the supplied key. String getUri() Get the URI associated with the profile. 6.10 SpaceResponse extends SpacePacket constructor SpaceResponse(SpaceChannel channel,int serial) SpaceResponseHeader getResponseHeader() Get the header associated with the response. Morton [Page 32] Technical Memo Java SpaceKit May 2000 6.11 SpaceRequest extends SpacePacket constructor SpaceRequest(SpaceChannel channel,int serial) SpaceRequestHeader getRequestHeader() Get the header associated with the request. 6.12 SpaceSep static String fetch(String subtree) static String fetch(String subtree,String element) Create a SEP fetch request with the subtree name and optional element. static String lock(String subtree) Create a SEP lock subtree request. static String release(int prevno) static String release(int prevno,String action) Create a SEP release subtree request with prevno and optional action. Valid contents for action are commit (which is the default) and rollback. static String store(String blockname,String action,String serial,String blockcontent) Create a SEP store request. Valid contents for action are create, update, write or delete. 6.13 SpaceServer constructor SpaceServer() throws SpaceException void close() Close the connection. This is best done in a finally clause within the same class as the connection creator. SpaceResponse connect() throws SpaceTimeout,SpaceException SpaceResponse connect(boolean wait) throws SpaceTimeout,SpaceException SpaceResponse connect(String localize) throws SpaceTimeout,SpaceException SpaceResponse connect(String localize,boolean wait) throws SpaceTimeout,SpaceException Connect to the server as defined by the class host and port. The Morton [Page 33] Technical Memo Java SpaceKit May 2000 default port number is 10288. void free(SpaceResponse response) Free a response by removing it from the response queue. String getHost() Get the host name for the connection. int getPort() Get the port number for the connection. SpaceResponse request(SpaceChannel channel,String request) throws SpaceTimeout,SpaceException SpaceResponse request(SpaceChannel channel,String request,boolean wait) throws SpaceTimeout,SpaceException Send a request on the specified channel with optional wait. SpaceResponse respond(SpaceChannel channel,String request) throws SpaceTimeout,SpaceException SpaceResponse respond(SpaceChannel channel,String request,boolean wait) throws SpaceTimeout,SpaceException Send a response on the specified channel with optional wait. A "respond" is the same as a "request" without the "" tags. It is used to respond to a SASL OTP challenge. void setHost(String value) Set the host name for the connection prior to connect(). void setPort(int value) Set the port for the connection prior to connect(). void setTimeout(int value) throws SpaceException Set the socket read timeout value in milliseconds prior to connect. Default is 600000 (10 minutes). void setTrace(boolean value) Set if tracing should be output to System.err. SpaceResponse start(SpaceChannel channel) throws SpaceTimeout,SpaceException SpaceResponse start(SpaceChannel channel,boolean wait) throws SpaceTimeout,SpaceException Start a channel on the specified channel with an optional wait. void trace() Morton [Page 34] Technical Memo Java SpaceKit May 2000 Output a trace frame to System.err. SpaceResponse wait(SpaceResponse response) throws SpaceException SpaceResponse wait(SpaceResponse response,long timeout) throws SpaceException Wait for a response to be complete with optional timeout in milliseconds. 6.14 SpaceSocket WARNING: The SpaceSocket class is for low-level access to the socket used by SpaceServer. This class should not be called directly when combined with the SpaceServer class. This is a utility class for writing your own driver. static int defaultTimeout = 600000; //10 minutes constructor SpaceSocket() void close() Close the socket. void connect() throws SpaceException Connect using the current settings. boolean getDebug() Get current debug setting. String getHost() Get host name for the socket. int getPort() Get the port number for the socket (default is SpaceSocket.defaultTimeout). String read() throws SpaceTimeout,SpaceException Read a line from the socket. int read(SpaceResponse response,int size) throws SpaceTimeout,SpaceException Read a specified number of bytes from the socket into the supplied response. void setDebug(boolean value) Set the debug setting. void setHost(String value) Set the host name prior to connect(). Morton [Page 35] Technical Memo Java SpaceKit May 2000 void setPort(int value) Set the port number prior to connect(). void setTimeout(int value) throws SpaceException Set the read timeout value for the socket (default is SpaceSocket.defaultTimeout). void write(String string) throws SpaceException Write the String to the socket. 6.15 SpaceTimeout constructor SpaceTimeout() constructor SpaceTimeout(String message) 6.16 SpaceUserCache constructor SpaceUserCache() String get(String name) Get an element using the already set authenticator and class. String get(String propertyClass,String name) Get an element using the already set class. String get(String authenticator,String propertyClass,String name) Get the specified element. void load(SpaceConfig config,SpaceProfile profile) Load the user cache as specified by the configuration and profile. void setAuthenticator(String value) Set the authenticator used by the get() methods. void setPropertyClass(String value) Set the property class used by the get() methods. void store(SpaceConfig config,SpaceProfile profile) Store the user configuration to the pathname supplied in the configuration. References [1] Rose, M.T., "The Blocks eXtensible eXchange Protocol", draft-mrose-blocks-protocol-02 (work in progress), April 2000. [2] Rose, M.T., "The Blocks Simple Exchange Profile", draft-mrose-blocks-exchange-01 (work in progress), April 2000. Morton [Page 36] Technical Memo Java SpaceKit May 2000 [3] mailto:blocks-request@invisible.net [4] http://mappa.mundi.net/ [5] http://xml.resource.org/profiles/BXXP/bxxp.html [6] http://xml.resource.org/profiles/SEP/sep.html Author's Address Frank Morton Invisible Worlds, Inc. 1179 North McDowell Boulevard Petaluma, CA 94954-6559 US Phone: +1 707 789 3700 EMail: fmorton@invisible.net URI: http://invisible.net/ Appendix A. Blocks Public License Blocks Public License Copyright (c) 2000, Invisible Worlds, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name, trademarks, or tradenames of Invisible Worlds, Inc., nor the names, trademarks, or tradenames of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INVISIBLE WORLDS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, Morton [Page 37] Technical Memo Java SpaceKit May 2000 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Morton [Page 38]