Getting Started

[back]

Starting the server

There are two ways to start the server: It is either started by use of the start scripts that come with the sources, or manually in a Java environment (e.g. in Eclipse or from within another Java application).

The simple way

Just click on the startServer.bat or startServer.sh. The classpath is configured automatically and the configuration is read from the configuration file.

The more complex way

If the server should be started from within another Java application, the configuration is also read from the sqlsconfig.properties, but it can be changed before starting the server and even after the server has been started. To change settings of the configuration, the singleton class Configuration provides the necessary setters and getters. The server itself is started by the method startServer() of the class Server. If e.g. the port of the server should be set to 3000 (instead of the default value 2525), the credentials of the database should be adapted and then the server started, this looks like this:

Configuration.getConfiguration().setPort(3000);
Configuration.getConfiguration().setDbUser("dbusername");
Configuration.getConfiguration().setDbPassword("secret");
Server.startServer();

The method startServer() starts the server synchronously, i.e. the processing of the program will wait until the server is started and will continue if the server is fully started. The server can also be stopped that can be done by the method stopServer() of the same class.

[top]

Establishing connection

After the server is up, a client can establish a connection to it. Since all access to a server is done by use of an instance of the class TupleSpace, the instantiation of this object also contains the connection to the server. The particular location of the server and the credentials for this connection can be passed to the constructor, but there are also comfortable shortcuts to use default values. If the no-arg-constructor is used, the TupleSpace instance tries to connect to a server running on 127.0.0.1 on port 2525, the username, password and spacename are "sqlspaces". All other constructors are used for setting non-default values to these parameters. If e.g. a connection to a server on the host 192.168.1.23 on port 3015 should be established, this looks like this:

TupleSpace ts = new TupleSpace("192.168.1.23", 3015);

Afterwards the methods of the reference ts can be used to access the space.

An SQLSpaces server may consist of several spaces, which are logically disjoint storage areas. Spaces are created and deleted implicitly, so whenever the first tuple is written or the last is deleted a space is created respectively deleted. There is no explicit operation to create or delete a space. There is no strict design strategy how to divide a system into several spaces. The advantage of disjointness is of course needed, if two systems use the same server and must not interfere with each other. Other advantages of spaces are the possibilities of versioning single spaces and space based right managament that are described later.

[top]

Simple operations

As the name "TupleSpace" implies, the basic data structure is the tuple, so first the structure of tuples need to be explained. A tuple consists of several fields, that have a value and a type. These are written in an ordered way into the space. The class Field has several constructors, but the most important one takes only one Object as a value and determines its type. Since the datatypes the SQLSpaces are supporting are exactly the Java datatypes, no mapping or explicit typing needs to be done. The creation of several fields is demonstrated in the following:

Field f1 = new Field("Hello"); // a string field
Field f2 = new Field(28);      // an integer field
Field f3 = new Field(false);   // a boolean field
Field f4 = new Field(0.7d);    // a double field

If the fields of a tuple were created, the tuple itself can be instantiated by passing all fields as parameters to the constructor of Tuple:

Tuple t = new Tuple(f1, f2, f3, f4);

As a more comfortable way, the fields do not need to be created before, but the values can also be passed directly to the constructor:

Tuple t = new Tuple("Hello", 28, false, 0.7d);

The reference t can now also be used for inspecting the tuple, e.g. the methods getFields(), getNumberOfFields(), getField(int i ) can be used to access the fields. It is also possible to add fields after the creation with the methods add(Object o) (add new field at the end) and insert(Object o) (add new field as first field).

After the creation of tuples, the most important and basic operations on the space are the operations write, read and take. The write operation puts a given tuple to the space and makes it therefore available for later queries:

ts.write(t);

For fetching tuples from the server, a so-called template tuple is needed. The fields of a template query can be the same as the ones of a normal tuple (which are called actual fields), but they can also contain so-called formal fields. A formal field is a field that has only a type, but no value and is created by passing an instance of java.lang.Class instead of a value to the constructor of Field or Tuple. The response of a query consists of all tuples that match a given template tuple and two tuple match, if both have the same number of fields and at each position of the first tuple the field of the second tuple has the same type and value. If formal fields were used, only the type needs to be equal to match. After definition of a template the operation take can be used to fetch a matching tuple. The following example demonstrates how the matching works:

TupleSpace ts = new TupleSpace();
ts.write(new Tuple("Alice", 25));
ts.write(new Tuple("Bob", 28));
ts.write(new Tuple("Carol", 31));

Tuple template1 = new Tuple(String.class, 28);
// only Bobs tuple matches, because only his ends with 28
Tuple returnTuple = ts.take(template1);
// returnTuple contains ("Bob", 28)

Tuple template2 = new Tuple(String.class, Integer.class);
// all three tuples match, because all have the same signature
Tuple returnTuple = ts.take(template2);
// returnTuple may contain each of the three

There are two basic operations to query the space: take and read. Both are used the same way. The difference is that the matching tuple in the first case is removed from the space, while in the latter case it stays on the server and only a copy is delivered to the querying client. If a template matches to several tupels one is randomly chosen.

[top]

Extended operations

Finding all matches

If all matches to a given template are to be found, take or read are not appropriate, because they only give one randomly chosen match. However, it is also possible to fetch all matches by the methods readAll and takeAll, which return not one tuple, but instead a tuple array. So this small adaptation of the last example looks like this:

TupleSpace ts = new TupleSpace();
ts.write(new Tuple("Alice", 25));
ts.write(new Tuple("Bob", 28));
ts.write(new Tuple("Carol", 31));

Tuple template2 = new Tuple(String.class, Integer.class);
// all three tuples match, because all have the same signature
Tuple[] returnTuples = ts.takeAll(template2);
// returnTuples contains all three

Blocking queries

If a query finds no matching tuple, the basic operations read and take return null-references and readAll and takeAll return empty arrays. However, it is also possible to state queries that wait until a matching tuple has been found. These so-called blocking queries are used by the methods waitToTake and waitToRead and also take the template as parameter. Optionally a second parameter can be passed over, that defines the time in milliseconds this blocking should last. If this time is up and no matching tuple has been found, these two methods also return a null-reference.

TupleSpace ts = new TupleSpace();

Tuple template = new Tuple(String.class, Integer.class);
Tuple returnTuple = ts.waitToTake(template, 5000);
if (returnTuple == null) {
        System.out.println("After 5 seconds no match!");
} else {
        System.out.println("Match was found!");
}

Updating specific tuples

It is of course possible to change the contents of a tuple with a take and a subsequent write operation. However, the SQLSpaces offer a more convenient way that also guarantees that exactly the tuple is updated, that you wish (and not another with the same signature). For this purpose all tuples in the space have a so-called TupleID, which identifies each of them uniquely. These ids were given to them at the time of the write operation and they are also the return value of TupleSpace.write). So this update operation takes on the one hand a TupleID of the tuple to be updated and on the other hand a tuple that should replace the old one.

TupleSpace ts = new TupleSpace();
TupleID aliceId = ts.write(new Tuple("Alice", 25));

ts.update(aliceId, new Tuple("Alice", 26));

Note: TupleIDs are only set, if a tuple was written into the database. If a tuple had to been written to the space, but was immediately taken by a waiting waitToTake command, this tuple never entered the database and therefore has no TupleID. This is no bug, since you would never be able to use this tuples id, because it is not in the space anymore. So there is actually no need for these tuples to have an id.

[top]