Using the SIRA_PRISE client package

Two principal uses can be made of the provided client package : it can be used to communicate with a SIRA_PRISE server from within a java program, or it can be used to do "scripting" : save SIRA_PRISE commands in a script file, and execute those commands repeatedly.

The first of the following subsections will give a general overview of programming in java for SIRA_PRISE with short examples, the second will discuss the scripting facilities provided by the client package. The full technical reference for using the SIRA_PRISE client package with a java program is in the javadoc documentation for this package, which is also included in the distribution.

  1. SIRA_PRISE & java programming

    There is conceptually little difference between accessing SIRA_PRISE from within java or accessing any other DBMS from within same : setup the connection/transaction, issue your commands, process the results, close connection/transaction. There are of course differences, but they are in the details.

    1. Getting a connection

      Getting a connection is done by creating a new DBConnection object

      DBConnection dbc = new OneShotDBConnection(new SiraPriseServer(host,port), "", null);

      The empty String for clientID and null for the Signer object will cause the connection to run "unauthenticated client" mode. For full detail on the options that can be set when creating a connection, pls refer to the javadoc. Note that "OneShotDBConnection" expresses the intent to also have a "PooledConnection" kind of thing, but that hasn't been implemented yet.

    2. Getting a transaction

      Getting a transaction within which commands can be issued is done by invoking a startTransaction(...) method on the DBConnection object :

      DBTransaction dbt = dbc.startTransaction(...);

      Information passed to the constructor includes, a.o. identity of the user owning the transaction (anonymous transactions are also possible of course). For full detail, pls refer to the javadoc.

    3. Executing queries

      Once the transaction is established, the object representing it can be used to execute a query, or multiple queries in one single go :

      Relation result = dbt.execQuery("RELVAR");

      Relation[] result = dbt.execQueries("SEMIJOIN(RELVAR,KEY)", "SEMIMINUS(RELVAR,KEY)");

      For full information, pls refer to the javadoc. E.g. all execQuery() methods also come in a flavour that immediately ends the transaction in the same go. Note that these examples are the simplest and oldest ways to specify the query, and the main disadvantage associated with this way of working is that syntax errors in the RA expressions cannot be detected at compile time (compile time of the java source, that is).

    4. Executing updates

      Updates are done in a similar fashion but using methods not named 'query' :

      Relation updateResult = dbt.execServerCommand(new ServerDeleteCommand("RELVAR","RELVAR( ... )"));

      Relation updateResult = dbt.execServerCommand(new ServerMultipleAssignmentCommand(new ServerDeleteCommand( ... ), new ServerAddCommand( ... ) ));

      The first example constructs a Delete command, specifying that the target of the delete is the relvar named RELVAR, and the tuples to be deleted are those that appear in the body of the relation that the second argument evaluates to. In this example that expression is a relation value selector, but it could also be any other relational expression, e.g. a RESTRICT on the target relvar :

      Relation updateResult = dbt.execServerCommand(new ServerDeleteCommand("RELVAR","RESTRICT(RELVAR,EQ(RELVARNAME, ... ))"));

      And once again, for full information, pls refer to the javadoc, in particular the complete hierarchy of possible ServerCommands. Also note that in the case of assignment commands, the returned relation will not hold any information. The return relation is only used meaningfully if the server command is a ServerInquireCommand.

    5. Ending the transaction

      If you did not invoke the "...AndEndTransaction" flavour of your query or update, then end the transaction now :

      dbt.end(true);

      'true' means commit, 'false' will result in rollback. Dedicated commit() and rollback() methods are obviously also available, but those will not end the transaction.

      The relevance of ending your transaction explicitly is that if the connection closes, it will end any 'dangling' transaction with a rollback, not with a commit.

    6. Closing the connection

      And to wrap up nice neat and complete :

      dbc.close();

    7. An alternative to specifying RA expressions as complicated strings

      When specifying queries, the LISP syndrome of the syntax might somewhat get in the way of productivity. An alternative also exists that is several times more verbose, but otoh might help make it easier to produce correct query expression strings and thus save time nonetheless. The basic idea is to write RA expressions more in native java-style, but only do so to "make believe" that this is RA being carried out :

      String queryText = new RelvarProxy(new SiraPriseServer(...), NameIdentifier.get("RELVAR")).getHostExpressionEvaluationText();

      IRelation query = new RelvarProxy(spServer, C_RELVAR).semiJoin(new RelvarProxy(spServer, C_KEY));
      String queryText = query.getHostExpressionEvaluationText();

      And once again, for full information, pls refer to the javadoc, in particular the complete hierarchy of the IRelation interface.

    8. The JSBA, an ORM-like alternative to the extensive stringbuilding involved in specifying updates

      Specifying updates the "ServerAddCommand way" as illustrated before, is still very tedious and boring. The JSBA makes things better :

      Relation insertResult = DmlCommand.execDmlCommand(new Relvar(relvarName, relvarPredicate).dbAddCommand());

      new Relvar(...) creates a Relvar object, where the Relvar class is written according to the JSBA requirements, and the .dbAddCommand() then gives the system a handle to the ServerAddCommand to issue ADD RELVAR,RELATION(...) to the server.

      And once again, for full information, pls refer to the javadoc, this time the DBObject and UpdatableDBOject interfaces, as well as their abstract implementations AbstractDBObject and AbstractUpdatableDBObject.

    9. Turning query results / Relation objects into JSBA DBObjects

      This is where the JSBA gets ORM-ey : after

      Relation result = dbt.execQuery("RELVAR");

      you can do

      Collection<Relvar> resultTheJavaWay = DBObjectFactory.getObjectCollection(result, Relvar.class);

      Since the Relvar class implements UpdatableDBObject, the setPreUpdateState() method will internally be invoked on each Relvar object created, and then after invoking some standard setter methods on such an object, say setRelvarPredicate(...), the Relvar object at hand can be used to initiate the database update that performs the same changes in the database that were carried out in the object :

      relvar.setRelvarPredicate(...);
      DmlCommand.execDmlCommand(dbt, relvar.dbUpdateCommand());

      or the delete

      DmlCommand.execDmlCommand(dbt, relvar.dbDeleteCommand());

      and the circle is closed :-) Note that while internally this method relies on getting the appropriate ServerCommand and pass that to the DBTransaction method, it is necessary to work via this new execDmlCommand method in order to keep the object's preUpdateState in sync with the state in the database.

    10. Writing JSBA classes

      A small futility exists to help you write JSBA classes (which might itself be perceived as a boring activity since it ultimately boils down to copying a spec alread written (the logical design of a relvar or set thereof) into some other language. See the DBObjectGenerator class for more details.

  2. Script processor ins & outs

    1. Invoking the script processor

      The java class for the script processor is be.SIRAPRISE.client.ProcessScript. The command line for invoking the script processor thus becomes

      java be.SIRAPRISE.client.ProcessScript scriptfilename

      Please observe that the classpath must be set properly, such that the script processor has access to the sp.client.jar package :

      java -cp sp.client.jar be....,

      where the name of the jar file might need to be preceded by the appropriate directory specification.

    2. Scripting run-time options

      Several options can be configured for invocations of the script processor. Which options they are, and where and how the script processor searches for them, is explained in this section.

      There are several ways to pass run-time options to the script processor. The order in which the script processor searches for these options is the same for each option, allthough for some options it may be the case that some of these locations do not apply :

      1. The arguments from the command-line that invoked the processor.
      2. A properties file to which the script processor has access.
      3. The default value defined for the option in question.

      Specifying options via the command line is done by appending the option name, followed by the option value in brackets, at the end of the command line. E.g. to set the HOST option to the value LOCALHOST via the command line, one would have to enter the command line

      java be.SIRAPRISE.client.ProcessScript ... HOST(LOCALHOST)

      Specifying options via a properties file is done by placing option lines in a file named be.SIRAPRISE.client.ProcessScript.properties.  This file is searched for (in the order specified) on the classpath, in the user's current working directory (i.e. the directory named by the user.dir system property), and in the user's home directory (i.e. the directory named by the user.home system property).

      Option lines have the format OPTIONNAME=OPTIONVALUE (e.g. HOST=LOCALHOST ).

      The following options can be used in the script processor :

      Option name Description Default value
      HOST The hostname or IP address of the SIRA_PRISE server to which to connect. LOCALHOST
      PORT The IP port number on the server to which to connect 50000
      USER The userID owning the transactions that will be started to execute the script's commands. "" (the zero-length string, implying that the transactions will be anonymous).
      OUTFILE The file where the script processor's output is to be written. This option can only be set on the command line, not in a properties file. The script file name, suffixed with ".out.txt".
      SCRIPTSDIRECTORY Directory where the script processor will search for a script file if it is not found in the current directory. No default.
      SIGNINGALGORITHMS Comma-separated list of names of algorithms that the script processor can use for signing. No signing.
      CRYPTOALGORITMHS Comma-separated list of names of algorithms that the script processor can use for encryption. No encryption.
    3. Script file location

      The script processor uses the following steps to locate the script file :

      1. Search the named script file in the current directory. If such a file exists, process this script.
      2. If the given script file name does not contain any directory or drive specification character, AND a scriptsdirectory run-time option has been defined, prepend the value of this sciptsdirectory option to the given filename and use that as the script file name.

      The following table clarifies how the script processor determines the complete name of the script file to read, based on the filename given in argument :

      Argument Value specified for property SCRIPTSDIRECTORY Complete filename searched for if script not found in the current directory
      Usr/MyScRiPt SCRIPTS\ Usr/MyScRiPt.SPS
      Scripts\test25 SIRA_PRISE_SCRIPTS\ Scripts\test25.SPS
      Test25.SPS SCRIPTS\ SCRIPTS\Test25.SPS
      Test25 SCRIPTS\ SCRIPTS\Test25.SPS
      X:myscript SCRIPTS\ X:myscript.SPS

      Note that names of script files must always end with the suffix ".SPS". The script processor will append this suffix to any provided filename that does not meet that requirement.

    4. Script file contents

      Scripts files are "plain text" files, encoded using some character set.  Which character set the script file is encoded in, can be specified as the first line in the script as follows :

      * Charset=windows-1252

      If this line is not found in the script, or the value specifies a character encoding that cannot be found using Charset.forName() , then the character encoding is assumed to be the default Character encoding of the JVM in which the script processor runs (i.e. the Character encoding returned by Charset.defaultCharset() .  (Note : this line is assumed to hold only characters in the unicode range < 128.  So the encoding of the line that defines the encoding for the rest of the file should not be an issue.)

      Scripts files contain series of valid SIRA_PRISE commands in the format as defined in the grammar section. The commands in the file are separated from each other by a CRLF sequence or a LF character, e.g. :

      add relvar,relvar(t(relvarname(DUMMADEE)relvarpredicate(The shop is open)))
      add key,key(t(relvarname(DUMMADEE)keyid(1)))

      The token combination |> (a vertical bar followed by a greater-than sign) at the end of a line is a continuation sequence. This indicates that the following line in the script will be part of the same command. That following line must be present, or the script processor will raise an exception and not send the command to the server.

      The script processor in the package processes a script file by reading this file line per line, assembling the lines into full commands according to the continuation sequences it encounters, and sending each command to a SIRA_PRISE server. When not using continuation sequences, multiple assignments must therefore be written down in their correct syntactical form, i.e. CMD(...)CMD(...)CMD(...)... , all of them on one single line.

      Thus trying to execute the little script above will produce the following results :

      Request : add relvar,relvar(tuple(relvarname(DUMMADEE)relvarpredicate(The shop is open)))
      Reply : be.SIRA_PRISE.server.ConstraintViolatedException : Relvar DUMMADEE has no Key.
      Request : add key,key(tuple(relvarname(DUMMADEE)keyid(1)))
      Reply : be.SIRA_PRISE.server.ConstraintViolatedException : A Key is defined for relation variable DUMMADEE but that variable does not exist.
      Reply : OK.

      For the script to work as (presumably) intended, it must be changed to :

      CMD(add relvar,relvar(t(relvarname(DUMMADEE)relvarpredicate(The shop is open))))CMD(add key,key(t(relvarname(DUMMADEE)keyid(1))))

    5. File names and output

      The output of the process appears in a file named in the OUTFILE option.

  3. Script Processor connections & transactions

    1. Error handling by the script processor

      The script processor will try to process all commands using one single transaction, i.e. it will connect to the server once, start a transaction, and then send it all the commands in the script. If any kind of error is reported by the server, then all updates will be undone by the server up to the most recent successful commit, and the transaction will be ended. The script processor then proceeds by starting a new transaction, and continues to send the remaining commands to the server.

      Commit commands can be used in the scripts to control which updates can be undone in the event of an expected or unexpected error signaled by the server (expected errors arise if scripts are used to test whether SIRA_PRISE correctly reports constraint violations etc.).

    2. Using the script processor in authenticated mode.

      By default, the script processor connects to a SIRA_PRISE server without authenticating itself, and uses anonymous transactions to execute the commands. This section explains the steps needed to use the script processor in authenticated mode, i.e. have the script processor authenticate itself when connecting to the server.

      The steps needed to achieve this are the following :

      • Choose an algorithm to be used in the authentication, e.g. MD5withRSA (this can be any signing algorithm supported by your client's JVM's JCE framework).
      • Using (e.g.) the keytool that came with your Java implementation, generate a keypair for the alias that is obtained by concatenating the script processor's predefined client name "SIRAPRISESCRIPTPROCESSOR" and the name of the chosen signing algorithm. The keystore file must be named "be.SIRAPRISE.client.ProcessScript.keystore". Both the keystore password and the key password must be "ProcessScript". The CN= part of the name must be equal to the script processor's predefined client name.
      • Using (e.g.) the keytool that came with your Java implementation, create an X.509 certificate holding the public key of the generated keypair.
      • Make sure that the certificate is registered on the server to which you will be connecting (please see the section on security for detailed info on how to register a client and its certificate(s)).
      • Make the keystore available to the script processor. This is done either by placing the keystore file in the directory from where the scripts are executed, or else by adding the keystore to a jar file that exists on the script processor's classpath.
      • Make sure the script processor will attempt to use the chosen signing algorithm by properly defining the SIGNINGALGORITHMS run-time option for the script processor.
    3. Using non-anonymous transactions with the script processor

      The steps needed for using non-anonymous transactions with the script processor are similar to the steps needed for using the script processor in authentication mode, with the following differences :

      • The alias for which to generate a keypair is now the concatenation of the userID and the chosen algorithm name.
      • The keystore and key passwords must both be "password".
      • The filename for the keystore file is now "xxx.keystore", with the xxx replaced by the actual userID.
      • The certificate to register on the server is a USERCERTIFICATE, not a CLIENTCERTIFICATE.