TOC |
|
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; there is also a developers' site.
TOC |
TOC |
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.
TOC |
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 //-------------------------------------------------------------------- SpaceResponse greeting = server.connect(true); //-------------------------------------------------------------------- // show valid profiles //-------------------------------------------------------------------- String[] profiles = greeting.getProfiles(); for(int k=0;k<profiles.length;++k) { System.err.println("Profile: " + profiles[k]); } //-------------------------------------------------------------------- // show localization //-------------------------------------------------------------------- System.err.println("Localize: " + greeting.getLocalize()); //-------------------------------------------------------------------- // show features //-------------------------------------------------------------------- System.err.println("Features: " + greeting.getFeatures()); //-------------------------------------------------------------------- // free the greeting //-------------------------------------------------------------------- server.free(greeting); //-------------------------------------------------------------------- // start a non-authenticated channel //-------------------------------------------------------------------- SpaceChannel channel = server.getChannel(SpaceProfile.sep); SpaceResponse response = channel.start(false); //-------------------------------------------------------------------- // build an sep fetch request while waiting for start response //-------------------------------------------------------------------- StringBuffer fetch = new StringBuffer(); fetch.append("<fetch>\r\n"); fetch.append("<union>\r\n"); fetch.append("<intersect>\r\n"); fetch.append("<compare subtree='doc.rfc' operator='contains' "); fetch.append("caseSensitive='false'>\r\n"); fetch.append("<path><element property='email' /></path>\r\n"); fetch.append("<value>mrose@</value>\r\n"); fetch.append("</compare>\r\n"); fetch.append("</intersect>\r\n"); fetch.append("</union>\r\n"); fetch.append("</fetch>\r\n"); //-------------------------------------------------------------------- // 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:
------------------------------------------------------------------ : 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 : ------------------------------------------------------------------ <response reqno='1' ttl='86400' creator="bxxp://sqa.invisible.net/"> <answers actualNum='1'> <rfc name="doc.rfc.2629" serial="2"> <rfc.props relativeSize="46132" number="2629" category="info"></rfc.props> <doc.props> <doc.front> <doc.title>Writing I-Ds and RFCs using XML</doc.title> <doc.author initials="M.T." surname="Rose" fullname="Marshall T. Rose"> <organization>Invisible Worlds, Inc.</organization> <address> <postal> <street>660 York Street</street> <city>San Francisco</city> <region>CA</region> <code>94110</code> <country>US</country></postal> <phone>+1 415 695 3975</phone> <email>mrose@invisible.net</email> <uri>http://invisible.net/</uri></address></doc.author> <doc.date month="June" year="1999"></doc.date> <doc.area>General</doc.area> <doc.keyword>RFC</doc.keyword> <doc.keyword>Request for Comments</doc.keyword> <doc.keyword>I-D</doc.keyword> <doc.keyword>Internet-Draft</doc.keyword> <doc.keyword>XML</doc.keyword> <doc.keyword>Extensible Markup Language</doc.keyword></doc.front><doc.extras abstract="true" note="false"></doc.extras> <doc.links> </doc.links> </doc.props> <remote.props uri="http://sqa.invisible.net/public/rfc/html/rfc2629.html"></remote.props> <remote.props uri="http://sqa.invisible.net/public/rfc/txt/rfc2629.txt" language="text"></remote.props> </rfc> </answers> </response> ------------------------------------------------------------------ : ID : Channel : Serial : Length : Frames : Complete : Error : ------------------------------------------------------------------ : 0 : 0 : 1 : 0 : 1 : Yes : No : ------------------------------------------------------------------
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<profiles.length;++k) System.out.println(profiles[k]);
The profile array generally contains the following profiles:
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
Localization and features are accessible via the getLocalize() and getFeatures() methods, respectively.
Establishing the connection can throw both a SpaceException and a SpaceTimeout.
When establishing the connection, an optional localization can be specified. The specification may include multiple languages. The order within the localize list is in priority order, left to right. If the server is unable to return messages in any of these languages, it defaults to "i-default."
The following is an example of connecting with three preferred languages, in priority order:
SpaceResponse greeting = server.connect("fr en en-us",true);
Once a connection exists, a SpaceChannel must be created to the server to make subsequent requests. Two types of channels exist: Non-authenticated and authenticated channels.
A non-authenticated channel is created by passing a valid non-authenticating profile to getChannel():
SpaceChannel channel = server.getChannel(SpaceProfile.sep);
The permission level of a non-authenticated channel is determined by the server.
Two types of SASL authenticated channels exist: Anonymous and OTP (One Time Password).
A SASL anonymous channel is created by passing a valid SpaceAuthenticator associated with the SASL anonymous profile:
SpaceAuthenticator authenticator = new SpaceAuthenticatorSasl(SpaceProfile.saslAnonymous); authenticator.set("authenticator","yourusernamehere"); SpaceChannel channel = server.getChannel(authenticator);
A SASL OTP autheticated channel is created by passing a valid SpaceAuthenticator associated with the SASL OTP profile:
SpaceAuthenticator authenticator = new SpaceAuthenticatorSasl(SpaceProfile.saslOtp); authenticator.set("authenticator","yourusernamehere"); authenticator.set("passphrase","yourpassphrasehere"); SpaceChannel channel = server.getChannel(authenticator);
Once a valid channel is open, requests may be made to the space server using the Blocks Simple Exchange Profile (SEP). A typical "fetch" sequence looks like:
StringBuffer sep = new StringBuffer(); sep.append("<fetch>\r\n"); sep.append("<union>\r\n"); sep.append("<intersect>\r\n"); sep.append("<compare subtree='doc.edgar' operator='contains' caseSensitive='false'>"); sep.append("<path><element property='state.of.incorporation' /></path>\r\n"); sep.append("<value>IN</value>\r\n"); sep.append("</compare>\r\n"); sep.append("</intersect>\r\n"); sep.append("</union>\r\n"); sep.append("</fetch>\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().
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:
InputStream in = response.getInputStream(); DOMParser parser = new DOMParser(); parser.parse(new InputSource(in)); DocumentImpl document = (DocumentImpl)parser.getDocument();
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);
Two exceptions are described in the Reference section: SpaceException and SpaceTimeout.
Tracing is a very helpful debugging tool to view the response queue set by doing:
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)
Description: Opens a channel with the SpaceProfile.saslAnonymous profile and the SpaceProfile.saslOtp profile.
Command Line Example:
java SpaceAuthenticate sqa.invisible.net blockmaster secretpassword
Description: Implements the Blocks Information Groper.
Command Line Example (returns list of valid "name" attributes):
java SpaceBig -server sqa.invisible.net search "<union>...</union>"
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 <store.xml
Command Line Example (delete the specified block):
java SpaceBig -server sqa.invisible.net -username yourusername -passphrase yourpassphrase store test.yoursubtree test.yoursubtree.2629 delete 1
Description: Opens a channel with the SpaceProfile.nullEcho profile and passes the text "Ping" as a normal request, which is echoed back. Secondly, opens another channel with the SpaceProfile.nullSink profile.
Command Line Example:
java SpacePing sqa.invisible.net
Description: Runs numerous tests, including using the Apache-Xerces parser.
Command Line Example:
java SpaceTest sqa.invisible.net
Description: Runs numerous tests.
Command Line Example:
java SpaceTest2 sqa.invisible.net
TOC |
SpaceBxxd requires a configuration file in order to start.
The configuration file defines general server parameters as well as the available profiles and their associated parameters. A typical configuration file contains the following parameters and profiles:
<?xml version="1.0"?> <!DOCTYPE config PUBLIC "-//Blocks//DTD BXXD CONFIG//EN" "http://xml.resource.org/blocks/bxxd/config.dtd"> <config> <bxxd port="10288"> <parameter name="homeDirectory" value="/usr/local/bxxd" /> <parameter name="moduleDirectory" value="modules" /> <parameter name="banner" value="sqa.invisible.net ready to go" /> <parameter name="logFile" value="logs/bxxd.log" /> <parameter name="debugLevel" value="none" /> <profile uri="http://xml.resource.org/profiles/sasl/OTP" module="sasl-otp"> <parameter name="authDirectory" value="auth/" /> </profile> <profile uri="http://xml.resource.org/profiles/sasl/ANONYMOUS" module="sasl-anon"> <parameter name="acl" value='subtree "" privs "fetch"' /> </profile> <profile uri="http://xml.resource.org/profiles/NULL/ECHO" module="null"> <parameter name="mode" value="echo" /> </profile> <profile uri="http://xml.resource.org/profiles/NULL/SINK" module="null"> <parameter name="mode" value="sink" /> </profile> </bxxd> </config>
This is a configuration file for the default port 10288.
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.
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:
Finally, restart inetd as super-user:
kill -HUP pid
Where "pid" is the process id of inetd.
A properties file defines users of the system. The location of the user properties file is found in the configuration file. Two 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.
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.
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:
<profile uri="http://xml.resource.org/profiles/NULL/REVERSE" module="reverse"> </profile>
If you are using the server in stand-alone mode, you will need to restart since the configuration file loads at start time
A module extends the class "SpaceModuleThead" as in the 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:
See "Module Structure" below and the reference section for a complete description of the SpaceModuleThread class.
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;k<argv.length;++k) { //--------------------------------------------------------------- // -portno //--------------------------------------------------------------- if(argv[k].equalsIgnoreCase("-portno")) { port = InvisibleUtil.parseInt(argv[++k]); continue; } //--------------------------------------------------------------- // host //--------------------------------------------------------------- host = argv[k]; } //-------------------------------------------------------------------- // instantiate the blocks server //-------------------------------------------------------------------- server = new SpaceServer(host,port); //-------------------------------------------------------------------- // connect to blocks server //-------------------------------------------------------------------- SpaceResponse greeting = server.connect(true); server.free(greeting); //-------------------------------------------------------------------- // start a reverse channel //-------------------------------------------------------------------- SpaceChannel channel = server.getChannel("http://xml.resource.org/profiles/REVERSE"); SpaceResponse reverseStart = channel.start(true); //-------------------------------------------------------------------- // make a reverse request //-------------------------------------------------------------------- SpaceResponse reverseResponse = channel.request("sdlroW elbisivnI"); System.out.println("Response: " + reverseResponse.toString()); channel.free(reverseResponse); channel.free(reverseStart); } catch(NullPointerException e) { System.err.println("java SpaceReverse [-portno port] host"); } catch(SpaceTimeout e) { System.err.println(e.toString()); } catch(SpaceException e) { System.err.println(e.toString()); } finally { //-------------------------------------------------------------------- // close the connection //-------------------------------------------------------------------- if(server != null) { server.close(); } } } }
Sample output from SpaceReverse:
Response: >tseuqer/<Invisible Worlds >'1'=onqer tseuqer<
All modules extend the class "SpaceModuleThread."
Since SpaceBxxp is a multi-threaded server, all modules must be 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:
See the reference for a full definition of each class.
To access the content of the request, a module uses "request.toString()."
Four types of responses are available to client requests:
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; }
A response() is the normal method to respond to a request.
String results = yourResultsMethod(); respond(results);
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.
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.
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");
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:
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.
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");
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.
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:
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:
To retrieve the authenticator from channel zero set by SpaceModuleSASLOTP:
String authenticator = getFromChannelZero("authenticator");
Any number of parameters by any name may be set.
TOC |
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.
This is the implementation of the "en-us" language as distributed with the Blocks SpaceKit for Java:
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); } }
TOC |
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
TOC |
constructor SpaceAuthenticatorSasl(String profile)
- Valid profiles include:
- http://xml.resource.org/profiles/sasl/ANONYMOUS
- http://xml.resource.org/profiles/SEP
- 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().
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.
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 "<request reqno=’n’>…</request>" tags. It is used to respond to a SASL OTP challenge.
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.
constructor SpaceException()
constructor SpaceException(String message)
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.
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"
- 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"
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()
- 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.
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 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()
- 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.
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.
constructor SpaceResponse(SpaceChannel channel,int serial)
SpaceResponseHeader getResponseHeader()
- Get the header associated with the response.
constructor SpaceRequest(SpaceChannel channel,int serial)
SpaceRequestHeader getRequestHeader()
- Get the header associated with the request.
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.
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 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 "<request reqno=’n’>…</request>" 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()
- 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.
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().
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.
constructor SpaceTimeout()
constructor SpaceTimeout(String message)
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.
TOC |
[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. |
TOC |
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/ |
TOC |
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:
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, 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.