CASSIUS 1.9 Motivations, Installation, and Usage


TOC

  1. Installation
  2. Terminology Used
  3. Understanding a CASS Awareness Environment
  4. Instrumenting Information Sources
    1. FileMonitor sample program
    2. CassDAV the CASSIUS enabled WebDAV Server
  5. Awareness Tools
    1. Sample Tools
    2. Using CASSandra Toolkit
    3. Modifying CASSandra Toolkit/Implementing your own Toolkit
    4. Building Awareness Tools Without CASSandra
  6. Sample Perl Code
  7. Full API
  8. Utilities
  9. Bugs

Installation

The following instructions assume you will use a Jakarta-tomcat server for running the servlet and mysql for the database server.

Install Tomcat Servlet Engine

First you will need to download the Tomcat servlet engine from the Apache Jakarta web site.

Once it has been decompressed, there are two techniques by which Tomcat 4.0 can be started:

* Via an environment variable:
  - Set an environment variable CATALINA_HOME to the path of the directory
    into which you have installed Tomcat 4.0.
  - Execute the shell command:

      %CATALINA_HOME%\bin\startup             (Windows)

      $CATALINA_HOME/bin/startup.sh           (Unix)

* By modifying your current working directory:
  - Execute the following shell commands:

      cd %CATALINA_HOME%\bin                  (Windows)
      startup                                 (Windows)

      cd $CATALINA_HOME/bin                   (Unix)
      ./startup.sh                            (Unix)

After startup, the default web applications included with Tomcat 4.0 will be
available by browsing:

    http://localhost:8080/

Further information about configuring and running Tomcat 4.0 can be found in
the documentation included here, as well as on the Tomcat web site:

    http://jakarta.apache.org/tomcat/

Downlaod and Install mysql

Download mysql binaries from the mysql web site

Once it has been decompressed, follow the steps in INSTALL-BINARY to install it.

If you have difficulty getting permission for your non root user account to access all of the databases, try a command like this executed as root:

mysql -e "grant all privileges on * to mkantor@localhost"
substituting your name for mkantor.

Download the source code

Source code can currently be obtained from http://www.ics.uci.edu/~mkantor/download.html

Downloading the Java Servlet version of the CASSIUS server will downlaod the file cass.tar.gz. Move this file to your tomcat server's webapps directory, decompress and untar it. The directory "cassius" should be created, containing a properly configured servlet.

> tar -xzf cass.tar.gz
You will need to either restart your tomcat server or instruct it to reload the cassius context. Tomcat README suggests creating a shell script for starting the server, below is an example:

#!/bin/sh
CATALINA_HOME=/usr/local/jakarta-tomcat-4.0.4
export JAVA_HOME=/Library/Java/Home
$CATALINA_HOME/bin/startup.sh
When you have started the server, change directory into cassius/WEB-INF:
> cd cassius/WEB-INF
Create the CASS database, and optionally a testing database called TESTCASS:
> [path-to-mysql-bin]/mysqladmin create CASS
> [path-to-mysql-bin]/mysqladmin create TESTCASS
Initialize the databases:
> [path-to-mysql-bin]/mysql -D CASS < InitializeGlobalTables
> [path-to-mysql-bin]/mysql -D TESTCASS < InitializeGlobalTables
If you want to test the installation (recommended), there are two approaches:

Simple Test

This very simple test will determine if the CASSIUS servlet is successfully loaded into your tomcat (or other) servlet engine, as well as testing that CASSIUS can access the mysql database. Enter the following URL into your web browser. Note that this must be all in a single line, and copy & paste can introduce new lines.

http://localhost:8080/cassius/cass?Operation=Register&GlobalPassword=Spanikopizza&Account=Test&AccountClass=TestClass&ApplicationPassword=testpass3

The response should be:
0:Account {TestClass}.Test Registered

For any other response, see below for possible causes.

The complete test suite

You shouldn't need to do this, and can only do it if you have the LWP::UserAgent perl module installed.
> cd ../testing
> perl testsuite.pl TEST_INIT.data
Execution should take a minute or two and end by printing "Successful finish!!!!!!!". For instructions on installing CassDAV the WebDAV server that sends notifications to CASSIUS, go to the CassDav section

Possible Problems

  1. Is your mysql database running locally and does the tomcat server have permission to access the TESTCASS and CASS databases?
  2. Is your tomcat installation running (is it accepting http requests)?
  3. Most likely source of problems: Tomcat needs to be restarted so that it can find cassius. And on my development machine, it helps to wait 10 seconds after restarting it before trying to access it.
  4. Look at your tomcat/log files to see what is going on. One of the log files will indicate that the servlet was not loaded, and will give reasons why. If the servlet is loading but not running, the problem is likely with mysql.
  5. Make sure that the cassius folder created within the tomcat folder is correctly spelled and in all lowercase letters.
  6. testsuite.pl was tested on Perl v5.6.0, but should work on any perl 5 interpreter. Perl errors can include incompatable perl versions, and the lack of the LWP::UserAgent module. This module can be found at www.cpan.org.
  7. If all else fails, email me at mkantor@ics.uci.edu

Terminology Used Here

Understanding a CASS Awareness Environment

The CASS strategy establishes a set of guidelines for implementing a usable awareness environment. Below we summarize aspects of these guidelines that will help in using this server.

1. Provide users with the ability to browse through lists of information sources to which they can subscribe

To provide an awareness environment in which users can monitor useful things in their work environment, there should be many sources of information that they can select from. Rather than assuming that users know what information sources are available, the technology needs to support their ability to browse to find sources of information that can affect their work.

2. Browsing for subsets of notifications

Given an information source, users need to be able to browse through the monitored objects, and the EventNames for those objects, and select which subset of those are relevant to that user's work. To support browsability, the list of monitored objects should be presented in a hierarchical fashion.

For example, in the case of a WebDAV server, monitored entities might consist of files, folders, system properties and system performance. Users then would browse through a hierarchical view of files and folders, selecting the files and folders that they want to monitor. Users may also browse through different system properties and select some so as to be notified when the server's configuration is changed. In the WebDAV scenario, the user may also browse through a list of different aspects of system performance and subscribe to be notified when load exceeds certain levels.

In this model, it is the responsibility of the information source to inform the awareness server of what objects it is monitoring, specifying where in the user browsable object hierarchy that entity exists, and maintaining that hierarchy as the information source's data changes. In a WebDAV server, as files and folders are created and deleted, the server must send object hierarchy manipulation commands to CASSIUS so that the awareness server's representation of the object hierarchy continues to match reality.

Note that these first points #1 and 2 are about providing users and tools with meta information. The impact of this meta information can be shown in this screen shot of the CASSandra subscription editor. On the top left is a hierarchy of information sources (which will be more impressive when we have more information sources). Users select the information source and are presented with a hierarchy of objects (in this case files and folders) which they can subscribe to. Selecting the file or folder does two things: it adds it to the formula being designed (right center panel of the user interface), and it finds all of the events that can affect the object and place them in the event hierarchy on the top right corner. Selecting the events adds them to the formula. Now imagine a user trying to create the subscription being designed up above without a user interface that provides this kind of meta information. However long it takes you to imagine it, it will take the user longer to do it correctly.

3. It is Important to Support Diverse Awareness Styles

Any technology that only supports a single style of awareness is limiting the kinds of people who can use the tool to those who not only work within an environment where that style of awareness is appropriate, but do not need to occasionally change to different work environments during the course of their work. If I instrument a WebDAV server to send out notifications of changes, I do not want that information to only be available to the subset of members in a work group for whom a specific awareness style is suitable, I want all people regardless of their work environment and work practices to have a means convenient for them by which they can receive the notifications.

The result of this goal is that a notification sent by any one information source should be easily interpreted and represented by a large number of awareness tools.

4. Any style of awareness tool should work with diverse information sources

A style of awareness that is effective within a particular work environment should not be limited to only a single source of awareness information. Awareness tools that can only be used with a single information source are very limited both in how they can help their user and in how effectively they can be used by other user groups (a side effect of this is that even if it is a very effective awareness style, people are less likely to find out that it exists and use it). To provide an effective awareness environment, users need to not only choose what awareness style they want to use (as discussed in the prior section), but also what set of information sources they will choose to monitor with that style. This has a variety of ramifications:
  1. It is important to provide a variety of styles of awareness tools that can fit within a variety of work environments. Once a style or set of styles is implemented, it can be reused to monitor any information source.
  2. A single awareness tool/style can be used not only to choose one of many information sources to monitor, but in fact to take awareness information from diverse sources and merge them together into a more complete picture of a work environment.
  3. A user can use multiple awareness tools in different styles monitoring diverse sets of information. A tool that uses peripheral sound can keep a person aware of activity in the work environment from one set of information sources, while more critical notifications are presented using a more intrusive style.
To achieve the goal of an awareness tool that can interpret and meaningfully represent notifications from diverse information sources, we use a fixed notification format with prespecified interpretations of the fields. The format is designed (and will need to evolve) to account for the needs of a variety of awareness styles. Information sources must use this format, and use the built in interpretation for the fields when they create and send out notifications.

Some of the more important fields include a textual summary for use in brief textual or verbal descriptions of an event, numerical quantification of an event for use in awareness tools that use graphing or intensity of sound/sight to communicate to the user. A URL field is used to obtain more data on the nature of the event.

There are also the EventName and GenericEventName fields. The EventName is the application specific name of the event, while the GenericEventName is a generalized event name that can be interpreted by users and awareness tools that have limited or no understanding of the activity or nature of the information source. Generic Events currently consist of:

For a WebDAV server, adding a file could result in an "Increase" generic event being sent to the parent folder (and optionally all parent folders up to the root of the object hierarchy), and an "Activate" generic event sent to the newly created object. Using the generic events guarantees that awareness tools that do not understand the events specific to an information source can have sufficient understanding to represent activity.

Instrumenting Information Sources

This section contains the steps I take when instrumenting information sources to work with the CASSIUS server. Examples and sample code are based on the file monitor in cassius/samples/filemonitor/index.pl. This script is a simple program for monitoring a directory structure for changes to modification dates of files and folders and sending notifications to the CASSIUS server each time they change. The directory structure being monitored is sent to the notification server so that users who ask the notification server what they can monitor will be presented with a copy of the file system (file/folder names only, not contents), allowing the user to browse the file system for the files that they want to monitor. It is assumed that the original file system is organized in a manner that will make sense to the user.

The File Monitor Sample

This sample program monitors a directory structure for changes to files/folders within it. It sends notifications when files are changed, created and removed. To run this program, first cd into the proper directory:
> cd ../samples/filemonitor
Next, execute the program "index.pl" using the following parameters:
> perl index.pl [directory to monitor] [URL to files] [URL to server] [Optional multiaccount mode]
Example:
>perl index.pl ~/cass "http://localhost:8080/~mkantor/cass"
The following sections describe the activities performed by this and other awareness tools. Activities are described at a high level, see API at the end of this document for more detail on how to give these commands. Bold font indicates messages being sent to the server in the form of: http://localhost:8080/cassius/cass?Operation=.....

1. Check to see if the account is already registered with the server

Information sources are assigned accounts by the server when the source registers itself with the server.. The account name is a function of the account name requested by the information source and one or more account class names which are used to categorize the information source. We first check to see if the name we would like to use is already in use. If it is we can:
  1. Choose a new name
  2. Assume that the name was used in a previous run of this program and reuse it. (we take this course).
  3. Register and request this already taken name, and be assigned a new name by the server that is based upon the requested name.
Determining if the account name is taken (without registering and being returned a name other than the one requested) is done using the ListAccounts command:
Operation=ListAccounts&AccountClass=FileMonitor

We request a list of all accounts that are in the "FileMonitor" account class (all accounts created by the filemonitor are assigned to the FileMonitor account class). This call returns a list which the program parses, searching to see if the desired name is already in use or not.

2. Register

If the account name is already in use then we will reuse that account, otherwise we need to register a new account.
Operation=Register&Account=cass&AccountClass=FileMonitor&GlobalPassword=Spanikopizza& ApplicationPassword=testpass3&URL=http://localhost/~mkantor/cass&Description=Monitored Folder

Because we are monitoring the folder named cass, the account name cass is used (Account=cass). The account class FileMonitor is used (AccountClass=FileMonitor). Passwords are assigned, permission to register information sources on the server requires a global password, and other information about the account is specified.

When this command is finished, the information source will have registered, an account will have been created for it on the server, and all future access to the notification server will be through that account.

3. Provide EventNames for Files and Folders

Next we provide a list of EventNames that are specific to files and to folders. We do not check to see if this has already been done; defining an event will overwrite any previous event with the same name and in the same account. We define two object types: File (with events Create, Delete and Modify), and Folder (with events Create, Delete, NewFile, ChildRemoved, ChildUpdated). Note that we are sending two operations in this one message to the server. There is no limit to the number of operations that can be strung together into a single command sequence. This is done to reduce the number of http connections that need to be established.
Account=FileMonitor.cass&Password=testpass3&Operation=DefineType&Type=File&
Event=Create&Definition=File added to file system&
Event=Delete&Definition=File removed from file system&
Event=Modify&Definition=Modification date on file has been updated&
Operation=DefineType&Type=Folder&
Event=Create&Definition=File added to file system&
Event=Delete&Definition=File removed from file system&
Event=NewFile&Definition=Folder has new file&
Event=ChildRemoved&Definition=Folder lost a file&
Event=ChildUpdated&Definition=File was updated in this folder

Note that these types are defined only for the account FileMonitor.cass. If we were to create a new FileMonitor account, these types would need to be redefined for the new account. Also, Each event has a definition associated with it as explanation to users who may wish to subscribe to the event.

4. Initialize Object Hierarchy: Checking to see if objects exist

Next, the file monitor traverse its directory structure and checks to see that for each file and folder there is a corresponding entry in the awareness server's object hierarchy. This might be the case if there has been a prior run of the FileMonitor on the monitored directory.
Account=FileMonitor.cass&Password=testpass3&
Operation=ListObjects&ParentID=/home/mkantor/src/cassius/cassServlet.java

This call requests a list of children of an object (objects beneath the specified object in the hierarchy). If it returns 0 or more results then the object exists, if it returns a not found error then it does not exist.

5. Create Object in Hierarchy

If the file or folder does not have an entry in the object hierarchy, create one.
Account=FileMonitor.cass&Password=testpass3&
Operation=NewObject&ObjectName=cassServlet.java&
ObjectID=/home/mkantor/src/cassius/cassServlet.java&
ParentID=/home/mkantor/cass/src/cassius&
Type=File& Description=A file in the monitored directory

This creates a new object named cassServlet.java with the unique identifier (ObjectID) of /home/mkantor/src/cassius/cassServlet.java. Note that if this were a webdav server, we would not use the path as the unique identifier as webdav is able to understand a file move/rename operation. In the case of a file move in webdav, the ObjectID remains the same and all subscriptions and notifications relevant to that file retain their integrity. FileMonitor perceives a file move operation as a file deletion and file creation, all notifications and subscriptions to the old ObjectID (file path) become broken (the server should probably do something with these notifications and subscriptions, but it does not at this time).

The new object is created as a subobject of /home/mkantor/cass/src/cassius, is of type File, and contains a description for the user to read when subscribing.

6. Monitor For Changes

Monitor the file system for changes. When a new file or folder is detected, send a new object (and any child objects if it is a new folder) to the server. Then optionally send an event indicating that the object has just been created. One may also send an event to the parent object indicating that the containing folder has just gained a new file. Using the propagate tag, we indicate that the grandparent of object and all ancestors within the hierarchy should also receive notifications. Thus, I can subscribe to the entire cass folder and be notified of any changes anywhere within the directory structure. The following example is the creation of the cassServlet.java file.
Account=FileMonitor.cass&Password=testpass3&
Operation=Notify&ObjectID=/home/mkantor/src/cassius/cassServlet.java&
Event=Create&GenericEvent=Activate&
Summary=/home/mkantor/src/cassius/cassServlet.java has been created&
URL=http://localhost/~mkantor/cass//home/mkantor/src/cassius/cassServlet.java&
NumericalValue=512

Account=FileMonitor.cass&Password=testpass3&
Operation=Notify&ObjectID=/home/mkantor/cass/src/cassius&
Event=NewFile&GenericEvent=Increase&
Summary=/home/mkantor/src/cassius/cassServlet.java has been added to this directory tree&
Propagate=1&URL=http://localhost/~mkantor/cass/src/cassius&
NumericalValue=512

The numerical value is the size of the file/folder (it should be the number of files within the folder).

Just as a file creation causes a Create/Activate EventName/GenericEventName to be sent to the child and NewFile/Increase EventName/GenericEventName to all ancestors, there is also a Delete/Deactivate/ChildRemoved/Decrease set of EventNames/GenericEventNames, and Modify/Change/ChildUpdated/Change events/genericevents.

CassDAV

CassDAV is a WebDAV server which sends CASSIUS notifications. What does this mean? Well, consider that modern operating systems (Mac OSX, and presumably the less modern Windows) can mount WebDAV servers on the desktop and treat the webdav directory just as it would any other folder. A group of people can all mount this directory on their desktop, copy files and folders to and from this directory, and even open and save files directly to the directory using any application.

Now consider what happens if every WebDAV event generates a CASSIUS notification. Every member of the work group can use awareness tools which will enable them to maintain awareness of which files and folders have changed or are being changed. No explicit effort is needed for users to send these notifications, they perform their work just as they always would have, saving their work to a mounted directory rather than a local directory, and all of the people who can be affected by the changes made (or not made) can now maintain awareness of the work going on in their work environment.

Given a range of awareness tools, different users can monitor these webdav folders in different ways. Depending upon different users needs, some of them may use peripheral awareness tools which make sounds or use peripheral vision to make the user aware of activity in different files and folders of the webdav directory. Others may want more active notifications that disrupt their work and explicitly tell them that a specific file has been changed. Still others may not want the information presented to them directly, but rather through the file system itself -- a few enhancements to a WebDAV client can enable it to represent the last set of notifications sent out concerning each file, including whether the file is currently locked and by whom. And people who are not directly working on the WebDAV directory but are affected by the progress of the work can use another awareness tool for subscribing to receive emailed digests summarizing activity each week.

Installation

First go to the cassius folder, which if you are using a Jakarta-Tomcat servlet engine should be in Tomcat's webapps folder. Then you will need to move cassdav out of the cassius folder, making it a high level webapps folder:
> cd $catalina_home/webapps/cassius
> mv cassdav ..
You will need to copy one file from WEB-INF into Tomcat's server/lib folder, and you will need to create a new database in the mysql database server.
> cd ../cassdav/WEB-INF
> cp servlets-cassdav.jar $catalina_home/server/lib/
> cp lib/mysql_comp.jar $catalina_home/common/lib
> mysqladmin create CASSDAV
> mysql -D CASSDAV < InitializeDatabase.txt
Next, go to the tomcat server's tomcat-users.conf and edit the usernames and passwords that control access to the tomcat server and as a result, the CassDAV servlet.
> cd $catalina_home/conf
> edit tomcat-users.conf

  <user name="username" password="userpassword" roles="tomcat" />

Finally, restart your tomcat server.

CassDAV can be accessed with the URL: http://localhost:8080/cassdav

Notes on Using CassDAV

Monitoring files vs. folders
When a user subscribes to monitor something (lets say a file), the user continues to be subscribed to that same file even if the file changes name or location. This has some positive and negative implications. If I move a file from a development folder into a private folder for modifications, everyone monitoring that file will know that it has been moved into a place where I can work on it exclusively, and can monitor the changes that I make to it. Similarly, if I move it to an archive or trash folder, everyone will follow this file to its new destination. This can be quite handy for tracking the course of work on a file through that file's lifespan. It can be a problem if it is moved somewhere else and replaced with a newer version. Presumably if you subscribed to monitor the file, you will really want to monitor the new version as well once the old one is moved to a trash folder. If the new one overwrites the old one, then that is what happens: the user is notified that the file they are monitoring has been updated and is now monitoring the new version. If the old file is moved, the subscription continues to track the original file.

An example of where this can be particularly irritating is if you use a program like emacs which saves backup copies as <filename>~. It turns out that emacs does not create <filename>~, but in fact moves <filename> to <filename>~ and then creates a new <filename>. If you were subscribed to <filename> and someone edits it with emacs, you will soon find yourself monitoring <filename>~ instead because it is the file that you requested to monitor, even though its relevance has been made obsolete by emacs' backup technique.

An alternate approach to monitoring a file where you do not need to receive every lock, unlock and other events is to monitor a folder that contains the file. The folder will receive most notifcations sent concerning changes to the file, but if the file is moved to a trash or archive folder and a new file replaces it, notifications will be sent saying that the file was moved to another location, and then all further notifications will concern the replacement file. Naturally, the folder itself can be moved...

Notifications

The following notifications can be sent to users who have subscribed to monitor a file:

WebDAV methodCASSIUS EventCASSIUS GenericEventDescription
GETGetActivateThe file has been downloaded either to a local file system or into the memory of an application such as emacs
PUTCreateActivateThe file has been uploaded or saved to the server, and there was not previously a file with this name. This should happen when either copying files to the CassDAV directory or when executing a save operation from an application.
PUTUpdateActivateThe file has been uploaded or saved to the server, and there was a version of the file that was overwritten by the new version. This should happen when either copying files to the CassDAV directory or when executing a save operation unless the save is performed by emacs which moves the file to be overwritten.
DELETEDeleteDeactivateThe file has been removed from the server.
COPYCopyIncreaseThe file has been copied to a second location on the server. A COPY WebDAV event also results in a Create event being sent to the newly created file.
MOVERenameChangeThe filename has been changed, but remains within the same folder.
MOVEMoveChangeThe file has been moved to a new folder.
LOCKLockActivateThe file has been locked for exclusive write permissions of a single user.
UNLOCKUnlockDeactivateThe file has been unlocked and any user can modify it.

Users who monitor a folder can receive any of the above file events and can also receive:

WebDAV methodCASSIUS EventCASSIUS GenericEventDescription
GETGetChildActivateAn item in this folder has been downloaded.
PUTFileUpdateActivateAn item in this folder has been uploaded.
PUTFileAddedActivateAn file has been uploaded into this folder.
PUTFolderAddedActivateAn folder has been uploaded into this folder.
DELETEFileRemovedDeactivateAn file has been removed from this folder.
DELETEFolderRemovedDeactivateAn folder has been removed from this folder.
MOVEChildRenameChangeA file or folder within this folder has been renamed.
COPYChildCopyChangeA file or folder within this folder has been copied.

Note that notifications sent about files will be observed not only by people monitoring that file or folder; all of these folder events will be propagated up to the parent folder, grandparent folder, and on up to the root folder.

This list of events actually is longer than it should be -- while very descriptive, it gives users more choices than they really want for designing a quick subscription. Suggestions are of course welcome.

Notifications contain a URL that links the user to the document that has been affected. However, these URLs work much better in applets where they utilize the web browser's ability to log onto a server. Java applications provided with this distribution do not provide a way of entering a username and password to access the CassDAV server and view the document.

Awareness Tools

The distribution for CASSIUS includes the CASSandra toolkit for building awareness tools, and includes three sample awareness tools.

< NAME='Samples'>EventList Tool

The first is called EventList, and was created primarily for CASS developers. Developers can use this to view the notifications that they have sent to the server. This can be helpful when debuging information sources:
cd cassius/cassandra
setenv CLASSPATH CASSandra.jar:$CLASSPATH (or whatever equivalent syntax your system uses to add a jar file to your classpath)
java EventList <usename> <URL to server>

simpleScroller Tool

The second awareness tool is called simpleScroller. This is a very simple tool for scrolling text across the screen.
cd cassius/cassandra
setenv CLASSPATH CASSandra.jar:$CLASSPATH (or whatever equivalent syntax your system uses to add a jar file to your classpath)
java simpleScroller <usename> <URL to server>
There is an applet version which can be openned using http://localhost:8080/cassius/cassandra/tickerapp.html

BiffArray Tool

The third tool is modeled on XBiff. It provides a row of Biffs, where the graphics within the icon shows the most recent event to come from the objects being monitored. Rather than a mailbox with flag up or down graphic, it shows the GenericEvent field of the most recent notification to be received. As there are five values of Generic Event, there are 5 images used to represent the different states.

Each biff can monitor a different source if information. For example, if one has six biffs, two could monitor files that you work with, two could monitor people, one could monitor activity on a chat group, and the last could monitor the state of your group's printer.

A related feature is that the user can configure each biff to make a different sound when it detects an event. This has two effects on usage:

  1. If the same type of event comes repeatedly, the graphic won't change, it is only through sound that the user becomes aware that the event repeated
  2. The user interface does not need to be visible to be useful
The user can associate different sounds with different biffs. If a Deactivate event is a bad thing to happen to a printer (meaning it has gone off line), then we can play appropriate sound effects for communicating that something bad has happened. This software distribution includes brief musical exerpts for positive and negative sounds, but these can be replaced with your own beeps, or sounds of printers breaking by adding sounds to the cassius/cassandra/sounds folder and adding the sound to the cassius/cassandra/sounds/sound.data file. Currently it only handles .wav format, though it is trivial to add handling for other formats. Note that if running locally, you will need your own sounds folder and can modify it at will. If running as an applet, modifications need to be made to the sounds folder on the web server.

Clicking on a biff when it has no subscription will open the subscription editor. Clicking on a biff when it does have a subscription will clear its latest icon (like clearing the original XBiff), so that the next time that same type of event occurs, you can see it change icons from empty to the icon for the type of event received. Right clicking on a biff allows you to edit the subscriptions, sounds, and make other changes.

Note that to access sounds and icons, it is currently required to run this from the cassandra directory.

cd cassius/cassandra
setenv CLASSPATH CASSandra.jar:$CLASSPATH (or whatever equivalent syntax your system uses to add a jar file to your classpath)
java BiffArray <usename> <URL to server>
There is an applet version which can be openned using http://localhost:8080/cassius/cassandra/biffapp.html.

Building Awareness Tools

There are two approaches to building awareness tools: 1) Utilizing the CASSandra toolkit to handle all interaction with the server, 2) Implement those interactions yourself. Approach #1 is highly recommended; you worry about the user interface, we worry about the server. Approach #2 is relevant if you want a very lightweight and simple awareness tool, if you want to implement an awareness tool in a language not supported by the toolkit, or if you wish to implement your own toolkit. Implementing your own toolkit is important if you are not working in Java. It is also possible to provide add new user interface components to the existing toolkit -- important if working in an environment that does not support Java swing (for example: J2ME).

Some quick definitions:
UsernameIdentifies the user who is subscribing to receive notifications from the server
Interface/InterfaceName (in the context of a message to the server)Identifies the awareness style that the user wants the notifications sent to. Each subscription is associated with a username and an InterfaceName, and when a client polls or subscribes, it must send both to the server.
Interface (in a Java context)A set of Java methods that must be implemented -- unrelated to Interface/InterfaceName as a message to the server

CASSandra Toolkit Tutorial

This section presents the implementation of a very simple awareness tool implemented using the CASSandra toolkit. Before presenting the tool itself, we will look at the various things that the tool depends upon.

Toolkit.NotificationListener Interface

The awareness tool must use the Toolkit.NotificationListener interface, and use the CASSandra toolkit's addNotificationListener(Toolkit.NotificationListener) so that the awareness tool can receive notifications.
// Interface for tools that want to be notified of new notifications
public interface NotificationListener {

      // Sends a vector of all of the new cassandra.Notification objects
      public void notificationsReceived(Vector inNotifications);

      // Notifies the awareness tool that the latest attempt to poll
      // for new notifications returned nothing.  Generally, this
      // will not be used, but is handy for tools that act once
      // every X seconds, and need to know if there was new data
      // to determine what action to take.  One could also implement
      // a thread within the awareness tool, but this approach lets the tool
      // act immediately when it knows what if any new data has arrived.
      public void notificationsNotReceived();

      // Returns a unique string that identifies the user interface style.
      public String getInterfaceName();
}

Toolkit Public Methods

public class Toolkit {
    public Vector getNotifications();

    public Notification[] getNotifications(int inLastXNotifications);

    public Toolkit(String  inUsername,  // documented later in this section
                   String  inServletURL,
                   int     inPollInterval,
                   Long    inSince,
                   int     inPolicy, 
                   int     inPolicyComparison, 
                   boolean inDebug);

    public void startPolling(); // Starts polling thread, call if using toolkit to help poll for notifications.

    public void sendMessage(String inMessage);  // Send message to the server.  Message must be of form: "Operation=xx&Param1=val1&Param2=...".  Message is sent in a separate thread so that the main thread of execution does not have to be concerned with delays in http requests.

    public Vector sendMessage(String inMessage, boolean waitForResponse); // Same as above, except that if waitForResponse is true, then the message and response are completed within this thread, forcing this thread to wait for completion.  The advantage is that this thread gets to examine the server's response which is returned in a Vector.  Each element of the Vector is a line of the response.
}

Awareness Tool Implementation

The following very simple awareness tool displays the latest notification in a label.
import javax.swing.*;
import java.util.Vector;
import cassandra.*;

class YourClass extends JFrame implements Toolkit.NotificationListener {

    // Keep an instance of the toolkit handy
    Toolkit mTK;

    // This label will display the most recent notification we have received
    JLabel  mNotificationDisplay;

    // This button will allow us to change what we are subscribed to
    JButton mSubscribeButton;

    // the main routine
    public static void main(String[] args) 
    {
      // Initialize a toolkit. Parameters explained in next subsection.
      Toolkit lTK = new Toolkit("mkantor",
                                "http://localhost:8080/cassius/cass",
                                 15,
                                 new Long(new java.util.Date().getTime()-1000*3600*2),
                                 Toolkit.DELETE_POLICY_MORE_THAN, 
                                 0,
                                 (args.length >= 3));

      // Initialize the awareness tool, passing it the toolkit
      YourClass lYourClass = new YourClass(lTK);

      // Start the toolkit's threads. (see section on Toolkit implementation for explanation of threads)
      lTK.startPolling();
    }

    // Constructor takes in the toolkit instance.
    public YourClass(Toolkit inTK) {

      // Give the window an appropriate name
      super(getInterfaceName());

      // Save the toolkit instance
      mTK = inTK;

      // Start listening for notifications
      mTK.addNotificationListener(this);

      // Initialize graphical objects
      mNotificationDisplay = new JLabel("Waiting for notification");
      mSubscribeButton = new JButton("Subscribe");
      
      // If the user clicks on the Subscribe button, open the subscription window.
      // It is for this call that we need to save the toolkit instance.
      // To provide your own subscription editing window, replace 
      // SwingSubscriptionHandler (distributed with CASSandra) with
      // your own implementation.
      mSubscribeButton.addActionListener (new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new SwingSubscriptionHandler(mTK,getInterfaceName()); 
            }
          });

      // Finish creating the user interface
      getContentPane().add(mNotificationDisplay,BorderLayout.CENTER); 
      getContentPane().add(mSubscribeButton,BorderLayout.SOUTH); 
      setSize(200,200);
      setVisible(true);
   }

   // When notifications are detected, display only the most recent notification
   public void NotificationsReceived(Vector inNotifications) {
       Notification n = (Notification)inNotifications.elementAt(inNotifications.size()-1);
       mNotificationDisplay.setText(n.getObjectName() + ":" + n.getSummary() + ":" + n.getEvent());
   }

   public void NotificationsNotReceived() {}

   // Return the interface name to be used when communicating with the server.
   public String getInterfaceName(){return "YourClass";}
}

Toolkit Constructor

    public Toolkit(String  inUsername,
                   String  inServletURL,
                   int     inPollInterval,
                   Long    inSince,
                   int     inPolicy, 
                   int     inPolicyComparison, 
                   boolean inDebug)
inUsernameUsername of the person who is using this toolkit to poll for and subscribe to notifications
inServletURLURL to the servlet. Likely value is: http://localhost:8080/cassius/cass but this isn't a very portable setting.
inPollIntervalIn seconds, how long should pass between each time that the toolkit polls for new notifications. 5 is on the very low side and will likely overload the server if many users are using this setting. 3600 is useful for tools that only need to notify people within an hour of an event occuring.
inSinceA Long specifying the number of seconds to pass since some date in 1970 when the time began. This value indicates how far back in time the awareness tool should look in order to gather initial data to present the user. This is helpful because 1) an empty interface is an uncompelling interface, 2) it allows users to get caught up on events that they have missed. If null is used, then this is equivalent to looking only at future notifications.
inPolicyThe Deletion policy indicates whether and if the toolkit should store notifications once it has reported them to the awareness tool. Notifications stored in this way can be retrieved with Vector v = mTK.getNotifications() and related methods. Deletion policy can be either:
  • Toolkit.DELETE_POLICY_OLDER_THAN the inPolicyComparison indicates how many minutes old a notification should be before it is deleted from the server. 0 results in notifications not being stored by the toolkit.
  • Toolkit.DELETE_POLICY_MORE_THAN the inPolicyComparison indicates how many notifications to keep at a time, only the most recent notifications are kept. If the value is 20, then only 20 notifications are stored by the toolkit. If the value is 0, then notifications are not stored by the toolkit
Note that the policy does not affect calls to notificationsReceived(Vector inNotifications). If 20 notifications are received, and the toolkit will only maintain the most recent 5, all 20 will be sent to notificationsRecived, but all calls to getNotifications will return only 5.
inPolicyComparisonValue to associate with the DeletionPolicy
inDebugif true, prints out a little debugging information

Notification Class

Once the awareness tool has the notifications, it will have an object supporting the following methods:
public class Notification {
    public String getObjectID();
    public String getObjectName();
    public String getObjectType();
    public String getObjectDescription();
    public long   getTimeReceived();
    public String getURL();
    public String getSummary();
    public float  getNumericalValue();
    public String getEvent();
    public String getGenericEvent();
}

Implementation Checklist

The key elements of using the toolkit then are:
  1. Implement the Toolkit.NotificationListener interface
  2. Construct an instance of the Toolkit
  3. Add the NotificationListener to the toolkit's list of notificationListeners.
  4. Give users access to the subscription edit window by calling new SwingSubscriptionHandler(toolkit,interface_name)

Complex Example

Lets say now that we have 3 buttons, each of which has a different subscription and each of whose text shows the latest notification to match that subscription. Now each button needs to be able to have a subscription associated with it and to be able to listen for notifications.
import javax.swing.*;
import java.util.Vector;
import cassandra.*;

class YourClass extends JFrame {

    // Keep an instance of the toolkit handy
    Toolkit mTK;

    // the main routine
    public static void main(String[] args) 
    {
      // Initialize a toolkit.
      Toolkit lTK = new Toolkit("mkantor",
                                "http://localhost:8080/cassius/cass",
                                15,
                                new Long(new java.util.Date().getTime()-1000*3600*2),
                                Toolkit.DELETE_POLICY_MORE_THAN, 
                                0,
                                (args.length >= 3));

      // Initialize the awareness tool, passing it the toolkit
      YourClass lYourClass = new YourClass(lTK);

      // Start the toolkit Poll thread.
      lTK.startPolling();
    }

    // Constructor takes in the toolkit instance.
    public YourClass(Toolkit inTK) {

      // Give the window an appropriate name
      super(getInterfaceName());

      // Save the toolkit instance
      mTK = inTK;

      // Create the three buttons, each of which can have its own subscriptions
      // and directly receive notifications from the toolkit.  Each listener
      // MUST have a unique InterfaceName, or they will share the same subscriptions.
      getContentPane().add(new CASSButton("InterfaceName 1"),BorderLayout.WEST);
      getContentPane().add(new CASSButton("InterfaceName 2"),BorderLayout.CENTER);
      getContentPane().add(new CASSButton("InterfaceName 3"),BorderLayout.EAST);

      setSize(300,150);
      setVisible(true);
   }

   // Inner class defines an awareness component
   class CASSButton extends JButton implements Toolkit.NotificationListener 
   {
      String mInterfaceName;

      // One way to insure that every CASSButton has a unique interface name
      // is to have some other class pass it a unique interface name.
      public CASSButton(String inInterfaceName) 
      {
         // Set the button label to reflect that no notifications have yet been received.
         super("No Notifications Received");

         // Save the name of this awareness component's Interface style.
         mInterfaceName = inInterfaceName;   

         // Add this awareness component to the notification listeners.
         mTK.addNotificationListener(this);

        // Clicking on the button allows users to change the button's subscriptions.
        addActionListener (new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new SwingSubscriptionHandler(mTK,getInterfaceName()); 
            }
          });
      }

      // When notifications are detected, display only the most recent notification
      public void NotificationsReceived(Vector inNotifications) {
         Notification n = (Notification)inNotifications.elementAt(inNotifications.size()-1);
         setText(n.getObjectName() + ":" n.getSummary() + ":" + n.getEvent());
      }

      public void NotificationsNotReceived() {}

      // Return the interface name to be used when communicating with the server.
      public String getInterfaceName(){return mInterfaceName;}
   }
}

CASSandra Toolkit Implementation & Modification

The Toolkit consists of two parts: the message handlers and the tools that utilize the message handlers (typically, this would be the the subscription editors and managers such as SwingSubscriptionHandler).

Message Handler: PollLoop Thread

The first part of the message handler is the PollLoop thread -- an inner class of Toolkit. Once every inPollInterval seconds (as specified in the Toolkit constructor), it pushes the message "Poll" on the message queue. This inner class also calculates the String that requests the latest notifications for all currently registered NotificationListeners.
Operation=Poll&Username=user&Interface=Style1&Operation=Poll&Username=user&Interface=Style2&....

MessageHandler Thread

The MessageHandler thread inner class monitors the message queue. When there are messages to be sent, the thread wakes up, sends them, and then calls the main class's responseReceived method which processes the response to the message.

Toolkit Interfaces

    public interface NotificationListener {
      public void notificationsReceived(Vector inNotifications);
      public void notificationsNotReceived();
      public String getInterfaceName();
    }
  
    public interface ObjectListener {
      public void objectsReceived(Vector inObjects);
    }

    public interface CASSEventListener {
      public void CASSEventsReceived(Vector inEvents, String inType);
    }

    public interface SubscriptionListener {
      public void subscriptionsReceived(Vector inSubscriptions);
    }

    public interface AccountListener {
      public void accountsReceived(Vector inAccounts);
    }

    public void addNotificationListener(NotificationListener inList);
    public void addAccountListener(AccountListener inList);
    public void addCASSEventListener(CASSEventListener inList);
    public void addObjectListener(ObjectListener inList);
    public void addSubscriptionListener(SubscriptionListener inList);
A class should Implement a subset of these interfaces and use these addxxxListener to enable the Toolkit to notify the class of responses to: ListAccounts, ListSubscriptions, ListObjects, ListEvents and Poll requests sent to the CASSIUS server. Other operations don't currently communicate responses to classes. SwingSubscriptionHandler implements all of these interfaces except for NotificationListener. These interfaces will be important for developers who want to provide their own subscription editor in Java.

Communication with CASSIUS

Using the Toolkit's method:
public Vector sendMessage(String inMessage, boolean waitForResponse);
You can enter strings of the form: "Operation=xx&Param1=val1&Param2=..." to be sent to the server. Alternatively, the string "Poll" is also supported and causes the tool to poll for new notifications to any of its current notification listeners.

If waitForResponse is true, the http request and response are performed by this thread, preventing it from further execution until it is complete. In this case, the server's response is returned directly to the caller as a Vector of Strings representing the lines of the response.

If waitForResponse is false, then the http request is sent to another thread for execution. In this case, the current thread can continue executing other tasks, but can not directly obtain a response. Responses can be obtained for data requests (ListAccounts, ListSubscriptions, etc...), by implementing the appropriate Listener interface and adding your object as a listener.

Implementing Awareness Tools Without CASSandra

If you want to create a simple awareness tool that has a built-in subscription (rather than user specified), or monitors a formula passed in by a command line interface (as is done in the sample program cassius/samples/simpleaware/text.pl), then you do not need the toolkit. This section discusses the sample program cassius/sample/simpleaware/text.pl which takes two parameters: the URL to the servlet and a formula to monitor. Use of this tool depends upon the LWP:UserAgent perl library which can be found on www.CPAN.org.
> cd samples/simpleaware
> text.pl "http://localhost:8080/cassius/cass" "[AND FileMonitor.cass.ObjectID = /Users/mkantor/cass/src/cassius/cassServlet.java, FileMonitor.cass.Event = Modify]"
The following steps are followed by this simple awareness tool.

1. Subscribe

Operation=Subscribe&Username=testuser&Interface=text&Method=Replace&
Formula=[AND FileMonitor.cass.ObjectID = /Users/mkantor/cass/src/cassius/cassServlet.java, FileMonitor.cass.Event = Modify]

Note first of all that if sending this as Get request (i.e. url-to-server?Operation=Susbscribe&...) that having an "=" in the middle of a parameter creates problems. To actually submit the above request as a Get request, it is necessary to replace the "=" with "%3D":
Operation=Subscribe&Username=testuser&Interface=text&Method=Replace&
Formula=[AND FileMonitor.cass.ObjectID %3D /Users/mkantor/cass/src/cassius/cassServlet.java, FileMonitor.cass.Event %3D Modify]

The Method parameter is set to Replace which instructs the server to replace any existing subscriptions for this user and interface with the new subscription. For more information about the Method parameter, see the API at the end of this document. The formula here specifies that

  1. It is looking for notifications from within the cass account of the FileMonitor account class,
  2. It is looking for notifications of changes to the object with the ID = /Users/mkantor/cass/src/cassius/cassServlet.java,
  3. AND that EventName must be "Modify".
The syntax here is misleading in that it implies that it is possible to have a subscription that spans different accounts: [AND FileMonitor.cass1.ObjectID = ID_1, FileMonitor.cass2.Event = Modify]. The language is designed to support this for the future, but does not support it now.

Poll

Once subscribed, an awareness tool can poll the server for new notifications by passing its username and interface name.
Operation=Poll&Username=testuser&Interface=text

This tells the server to look for all notifications that A) Match any of the subscriptions for this username and interface, and B) Have arrived since the last Poll operation. You will note that there is no authentication currently used to prove that this really is the specified user. Security is an issue that is only marginally handled in this implementation of the CASSIUS server.

Responses come in the form of

0:Returning 2 Results
AccountClass.AccountName
ObjectName
ObjectID
Event
GenericEvent
Textual Summary
Numerical Value
URL to Information source
URL to more information about this event
Next AccountClass.AccountName, etc...

Sample Perl code

Operations are sent to the servlet using HTTP GET and POST methods. It does not matter which method is used. The sample perl code shown here for accessing the server uses the "URLLib.pm" perl library (which in turn depends upon the LWP:UserAgent perl library from www.cpan.org) -- however these libraries are not required, all that is required is code that can send http requests and get back the response. To use the "URLLib.pm" library, Copy cassius/testing/URLLib.pm into your working directory. In your Perl script, you will then have the following lines:
require "URLLib.pm";
local($Connect) = new URLLib();
$servlet_URL = "http://localhost:8080/cassius/cass";
$query = "Operation=......";
local($Response) = $Connect->Post($servlet_URL,$query);
if ($Response =~ /^0/) {
   print("Operation Accepted, response code began with a 0");
} else {
   print("ERROR: $Response");
}

Note that you can also use:
local($Response) = $Connect->Get($servlet_URL,$query);

Using CASSandra for information sources

CASSandra can also be used by information sources. The primary benfits of this are:
  1. You do not need to implement code for sending http requests, nor worry about creating threads that allow the message passing to happen outside of your normal execution thread.
  2. Future enhancements to the toolkit will replace strings of "Operation=x&Account=y..." with method calls that will generate these strings for you.

API

Note that multiple operations can be strung together and sent to the server with a single HTTP request. When performing this type of operation, it is not necessary to repeat Account or Password parameters, if these are used in the first operation in an HTTP request, then any following operation that does not use these parameters will be assumed to have reused the values from the first operation.

Operation=NewObject&Account=FileMonitor.cass&Password=testpass3&ObjectID=1&
Operation=NewObject&ObjectID=2

Account Management APIs

The following commands are used to register, modify and unregister information sources. Also in this section is a command for obtaining lists of available information sources. When an information source is registered, it becomes visible to users, and can start receiving notifications, lists of objects and events used by the information source.

An account consists of an account name and zero or more account classes that categorize the account. All access to the account is in the form [AccountClass].[AccountName] where the account class is any of the account's classes. For example: FileMonitor.cass. If the account has no class, then it is simply [AccountName].

Register Account

ParameterDescription
Operation Register
Account The name that the information source will use to access its information in the server. Can have spaces and some special characters.
AccountClass 0 or more names, comma separated, categorizing the account. AccountClasses can be created on the fly -- all that one needs to do to create a new AccountClass is to place it in this list. The classes are used to organize accounts so that all accounts that are created by the FileMonitor will be in the same class of accounts, all accounts created by tools used by the user mkantor may be in the class mkantor, etc... This is not a great mechanism, but does provide some order to help users who are browsing for relevant information sources.
GlobalPassword This password confirms that an information source (or its owner) is permitted to register itself with the CASSIUS server. Administrators can set this password in cassius/WEB-INF/Config.data. Default value is Spanikopizza.
ApplicationPassword Sets the password that will be used to confirm that future accesses to this account come from the information source and not a third party. Note that this is currently not encrypted, and serves as a place holder for a more secure form of password.
URL Optional URL that links users to the information source.
Description Optional description of information source, displayed when user browses through the list of accounts.
Table 1. Register new information source with Notification Server
Note that this request differs from other information source requests in that it does not refer to an existing account. Therefore, Account is only the account name and not the account path (i.e. AccountClass.AccountName)

Responses

Permission denied errors in the current version of the server are due to incorrect Global Passwords. Account Exists errors are non-critical and result in automatic assignment of a new name based upon the requested name. This behavior can be avoided by using the ListAccounts API to check to see if the name exists in the desired accounts. An account exists if the account name exists in any of the account classes requested for this account. If no account classes were requested, then the account exists if there is another classless account with the same name. The '{' and '}' characters in the Account Registered response are not EBNF or symbolic -- those are the actual characters returned. [xxx] indicate some kind of value which is only optional if explicitly stated. Account Must Have Value indicates that the Account parameter was used, but that its value was "". No Account Name indicates that there was no account name parameter.

Example

Register the account FileMonitor.cass with the server
local($AccountName) = "cass";
local($response) = $Connection->Get($servlet_URL,
                                    "Operation=Register&".
                                    "Account=$AccountName&".
                                    "AccountClass=FileMonitor&".
                                    "GlobalPassword=Spanikopizza&".
                                    "ApplicationPassword=testpass3&".
                                    "URL=http://localhost/~mkantor/cass&".
                                    "Description=Monitored Folder");

if ($response =~ /^7:Account Exists in classes: (.*), Assigned name is (.*)\.(.*)/) {
   $AccountName = $3;
   print("Name unavailable, assigned name is $AccountName\n");
} elsif ($response =~ /^0/) {
   print("Registered!\n");
} else {
   print ("FAILED!\n");
}

UnRegister

ParameterDescription
Operation UnRegister (note arbitrary capitalization)
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is not class then just AccountName
Password Password for accessing this account
Table 2. Unregister an information source from a notification server
This operation deletes all data related to the information source.

Responses

Example

Remove all information concerning FileMonitor.cass from the server
local($response) = $Connection->Get($servlet_URL,
                                    "Operation=UnRegister&".
                                    "Account=FileMonitor.cass&".
                                    "Password=testpass3&");
unless ($response =~ /^0/) {
   print ("FAILED!\n");
}

Change Account Information

ParameterDescription
Operation ChangeRegistration
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
URL Optional To change the URL for the information source, use this parameter.
NewPassword Optional To change the password for the account, use this parameter.
Name Optional To change the Name for the account, use this parameter. Note that naming conflicts will be checked, and a new name may be assigned by the server.
Classes Optional To change the account class list for the account, use this parameter. Note that naming conflicts will be checked.
Description Optional To change the description of the information source, use this parameter.
Table 3. Change the account information
Note that changing either the account name or class may result in a naming conflict due to the name already being in use within an account class. Naming conflict results in a new name being automatic assigned.

This operation is important because the nature, role and location of a source of information can move. For example, if the source of information is a document being monitored, the document can change from being a rough draft to a draft ready for review, it may be passed around, and edited on different machines, etc...

Responses

Note that if the name parameter is specified, it must have a value. The empty string can not be assigned as a name.

Example

Change the FileMonitor.cass account so that it is now a member of the following classes: 1) Not a FileMonitor, and 2) New Class. Also change the URL and description.
local($AccountName) = "new name";
local($response) = $Connection->Get($servlet_URL,
                                    "Operation=ChangeRegistration&".
                                    "Account=FileMonitor.cass&".
                                    "Password=testpass3&".
                                    "Name=$AccountName"
                                    "Class=Not a FileMonitor,New Class&".
                                    "URL=http://new.url.com"
                                    "Description=New description");

if ($response =~ /^7:Account Exists in classes: (.*), Assigned name is (.*)\.(.*)/) {
   $AccountName = $3;
   print("Name unavailable, assigned name is $AccountName\n");
} elsif ($response =~ /^0/) {
   print("Registration Changed\n");
} else {
   print ("FAILED!\n");
}

Listing Accounts

ParameterDescription
Operation ListAccounts
AccountClass Optional A single account class, if specified, returns a list of accounts within this account class. If unspecified, returns all accounts. If this parameter is used, and passed an empty string, then it returns all accounts that have no account class:
Operation=ListAccounts&AccountClass=FileMonitor returns all FileMonitor accounts
Operation=ListAccounts returns all accounts
Operation=ListAccounts&AccountClass= returns all accounts with no account class

Note that this parameter can not be used with the Hierarchical parameter nor the IgnoreAccounts parameter.

Hierarchical Optional if parameter is used (with any nonempty string as the value: Hierarchical=1) indicates that the accounts should be listed hierarchically, showing account classes, followed by the list of accounts within that class. If an account is a member of multiple account classes, it will be returned multiple times, once for each account class. If this parameter is not used, then all accounts are returned only once, and are not grouped by account class. In this case, AccountClasses are not listed except as part of the description for the class.

Note that this parameter can not be used with the AccountClass parameter nor the IgnoreAccounts parameter.

IgnoreAccounts Optional List only the account classes if this parameter has a value (Operation=ListAccounts&IgnoreAccounts=1)

Note that this parameter can not be used with the AccountClass parameter nor the AccountClass parameter.

Table 4. List Accounts
This functionality has two purposes. First, it supports the goal of users browsing through information sources to which they may want to subscribe. Second, it can be used by information sources to determine if they have registered with the server, and perhaps even find out what other information sources are part of their environment.

Responses

Note that we show how account classes and accounts will be formatted IF they are returned, but some requests only return one or the other.

Example

Determine if FileMonitor.cass has already been registered
local($DesiredAccountName) = "cass";

local($result) = $Connect->Get($servlet_URL,
                               "Operation=ListAccounts&AccountClass=FileMonitor");
if ($result =~ /0:Returning (\d+) Results/) {

   # In Perl, $1 contains the value of (\d+) above -- some number of digits
   local($resultcount) = $1;

   # Read the result string into an array, one line of the response per array element.
   local(@results) = split(/\n/, $result); 

   # Delete the first element of the array which is the 0:Returning x Results
   shift(@results);

   for ($i = 0; $i < $resultcount; $i += 3) {
     local($AccountName,$AccountClasses,$Description) = ($results[$i], $results[$i + 1], $results[$i + 2]);

     if ($DesiredAccountName eq $AccountName) {
        print("$DesiredAccountName is in use");
        return;
     }
   }
   print("$DesiredAccountName is available");
} else {
  print("ListAccount operation has failed: $result");
}

Object Management APIs

The object hierarchy, exists so that users can browse through a hierarchy of objects representing objects, properties and other things that an information source monitors and sends notifications about. Support for browsing greatly simplifies the task for users to discover the information that is of use to them.

In order to create this hierarchy, information sources must send objects to the server using the NewObject command, and place the object in the hierarchy. Each object has a ParentID property which gives the ObjectID of the object that they are contained within. If there is no parent, then the object is at the top of the object hierarchy. The parent object can be either a monitored object, or simply a place holder used to construct a hierarchy that will have meaning to the users.

For example, a section of a document is a monitorable object -- an object will be created on the server to represent it and act as the target for notifications sent from the information source to the server. It may also have subsections represented by subobjects. Both object and parent have meaning here and structuring them in this way should be intuitive to users familiar with document structures (i.e. all users). If what the information source is monitoring is the amount of load on the machine, an object representing this concept will sldo be sent to the server. In this case, it may not be as clear what the parent of such an object should be, but it and other aspects of performance monitored by the information source may all be grouped under an object called "Performance". The performance object has no real meaning in that it does not receive notifications, but is used solely to structure the information for the user. This distinction is not specified to the server when creating or using the objects.

Any object manipulation operation that affects a parent object can affect the children. Deleting or moving an object will delete or move all of its children.

Creating New Objects

ParameterDescription
Operation NewObject
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
ObjectName Name of the object to create. Used to enable the server to present readable lists of objects to the user. This supports the goal of allowing users to browse for information that they may subscribe to.
ObjectID A unique identifier that remains constant even if the name of the object should change.
ParentID Optional A unique identifier that informs the notification server of the object's parent or enclosing object. If not used or if used to pass an empty value, then no parent object exists. Objects with no parent are the highest level objects in the hierarchy.
Position Optional In cases where there are many objects contained by a parent object, the information source may feel that the order of these objects is important, in which case it will specify each object's position in the list of objects that are contained by the parent. If Position is not used, then the new object will be the last object in the list. Position=0 places the object as the first item in the list. An example of where this is useful is for representing the sections of a document. The order of the sections communicates their meaning, if Introduction comes after conclusion, users may assume that it is not the kind of introduction that they are looking for.
Type Optional type name. Type specifies a set of event names for the object, if left unspecified, object uses generic events only.
Description Optional description of the object's purpose and meaning.
Table A-8. Create a new object in the object hierarchy
Inform the notification server that there is a new object being monitored, giving it appropriate information to help the server associate changes with that object, and additional informatino to help users find, recognize the usefulness of and subscribe to the object.

Responses

Example

Create two objects: a folder to represent /home/users/mkantor/cass/src, and a file within that folder to represent cassServlet.java.
local($result) = $Connect->Get($server_URL,
                               "Operation=NewObject&".
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "ObjectName=src&".
                               "ObjectID=/home/users/mkantor/cass/src&".
                               "ParentID=/home/users/mkantor/cass&".
                               "Type=Folder&".
                               "Description=Folder in monitored directory&".
                               "Operation=NewObject&".
                               "ObjectName=cassServelet.java&".
                               "ObjectID=/home/users/mkantor/cass/src/cassServlet.java&".
                               "ParentID=/home/users/mkantor/cass/src&".
                               "Type=File&".
                               "Description=File in monitored directory");

if ($result ne "0:Object /home/users/mkantor/cass/src Created\n0:Object /home/users/mkantor/cass/src/cassServlet.java Created\n") {
   print("Operation Failed, output was: $result");
} else {
   print("Two objects created");
}

Remove Object

ParameterDescription
Operation RemoveObject
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
ObjectID Identify the object to be deleted using its unique identifier.
Table 5. Remove object from object hierarchy
This operation deletes the specified object and all of its children.

Returns

Example

Remove the object representing cassServlet.java
local($result) = $Connect->Get($server_URL,
                               "Operation=RemoveObject&".
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "ObjectID=/home/users/mkantor/cass/src/cassServlet.java");
if ($result =~ /^0/) {
   print("Object removed");
} else {
   print("ERROR: $result");
}

Modify Object

ParameterDescription
Operation ModifyObject
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
ObjectName Optional new name for the object
ParentID Optional Unique identifier for the object's new parent or enclosing object. If this parameter is not used, then the ParentID is unchanged. If the parameter is used, but no value is passed (Operation=ModifyObject&ParentID=&Account=FileMonitor.cass&...), then the ParentID is set to "", and the object is placed at the highest level of the object hierarchy.
Position Optional Specifies the position of the object in the new or existing parent object, ordering it with respect to its siblings in the hierarchy.

If ParentID is specified and Position is left unspecified, then the object is placed last in the list of contained objects. If only Position is specified, then the position within the current parent is changed. If niether is specified, then the object hierarchy is not changed.

Type Optional Assigns the object to be a different type, receiving a different set of events.
Description Optional change the description of the object
Table 6. ModifyObject command for moving and redefining objects

Responses

Example

Move the object cassServlet.java to a backup directory, and rename it to cassServlet.java.backup
local($result) = $Connect->Get($server_URL,
                               "Operation=ModifyObject&". # the next four lines identify the object
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&". 
                               "ObjectID=/home/users/mkantor/cass/src/cassServlet.java&".
                               "ObjectName=cassServlet.java.backup&". # the next three lines indicate what to change
                               "ParentID=/home/users/mkantor/cass/backups&".
                               "Description=Backup File");
if ($result =~ /^0:/) {
  print("Object Modified\n");
} else {
  print("ERROR: $result\n");
}

Note that this example demonstrates why an ObjectID should not be based upon information about an object that might change. This operation reflects the action of moving a file from the source directory to the backup directory, renaming it during the move. A new ObjectName is assigned, as well as a new parent and description. But the ObjectID not only still assumes that it is in its old location, but will also prevent new objects from being created with that same name -- no more cassServlet.java objects can be created.

List Objects in Account

ParameterDescription
Operation ListObjects
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Depth Optional Integer indicates the depth within the hierarchy to display.
0: This is the default value, and shows only the children of the selected parent.
1: Show children and grandchildren of selected parent
n: Show the hierarchy down to depth of n
ParentID Optional Indicates the object whose children we want to see. If not used, lists the highest objects in the object hierarchy. If Depth parameter has a high value, this will display the entire object hierarchy.
Table 7. Lists objects that users can subscribe to
Note that no password is required for this operation because it is only a request for information, not an attempt to modify data.

Responses

Note that if there is an object type, say "File", the response woud be:
[File]/home/users/mkantor/cass/src:/home/users/mkantor/cass/src/cassServlet.java:cassServlet.java
and if there is no object type, then:
/home/users/mkantor/cass/src:/home/users/mkantor/cass/src/cassServlet.java:cassServlet.java
Note that we return the number of children so that if depth is less than the total depth of the tree, it will be known if there is greater depth that was not retrieved or if that is a leaf in the hierarchy.

Example

Determine if there is an object named cassServlet.java within the object /home/users/mkantor/cass/src
local($result) = $Connect->Get($server_URL,
                              "Operation=ListObjects&".
                              "Account=FileMonitor.cass&".
                              "ParentID=/home/users/mkantor/cass/src");
if ($result =~ /^0:Returning (\d+) Results/) {

   # In Perl, $1 contains the value of (\d+) above -- some number of digits
   local($resultcount) = $1;

   # Read the result string into an array, one line of the response per array element.
   local(@results) = split(/\n/, $result); 

   # Delete the first element of the array which is the 0:Returning x Results
   shift(@results);

   for ($i = 0; $i < $resultcount; $i += 2) {
       local($AccountIDInfo,$Description) = ($results[$i], $results[$i + 1]);
       $AccountIDInfo =~ /^(\[.*\])?(.*):(.*):(.*)$/;
       
       local($ObjectType, $ParentID, $ObjectID, $ObjectName) = ($1,$2,$3,$4);
       if ($ObjectName eq "cassServlet.java") {
          print("Found it!");
          return;
       }
   }
   print("Not found!");
} else {
   print("ERROR: $result");
}

Working with Types

Types are nothing more than a type name and a series of EventName, Description pairs. Their current purpose is to inform awareness tools of what types of events an object can be affected by. This enables users of awareness tools to create more meaningful subscriptions. A type is associated with an account, which means that if the CASSIUS server has 500 accounts in the FileMonitor class, then we will need to send type definitions 500 times.

Defining Types

ParameterDescription
Operation DefineType
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
Type Name of the type being defined
Event Name of the event which objects of this type may receive
Definition Description of the event.
Table 8. Define a new Object Type
Note that you can have as many Event=x&Definition=y pairs within a single operation as is needed.

Responses

When a type is defined, any previous definition for that type is automatically deleted, there is no need to explicitly use the RemoveType command to redefine a type.

Example

Define two types: a file type and a folder type.
local($result) = $Connect->Get($server_URL,
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "Operation=DefineType&".
                               "Type=File&".
                               "Event=Create&Definition=File added to file system&".
                               "Event=Delete&Definition=File removed from file system&".
                               "Event=Modify&Definition=Modification date on file has been updated&".
                               "Operation=DefineType&".
                               "Type=Folder&".
                               "Event=Create&Definition=File added to file system&".
                               "Event=Delete&Definition=File removed from file system&".
                               "Event=NewFile&Definition=Folder has new file&".
                               "Event=ChildRemoved&Definition=Folder lost a file&".
                               "Event=ChildUpdated&Definition=File was updated in this folder");
if ($result eq "0:Type File Created\n0:Type Folder Created\n") {
   print("Types file and folder created");
} else {
   print("Error");
}

Remove Types

ParameterDescription
Operation RemoveType
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
Type Name of the type being deleted
Table 9. Delete an Object Type

Responses

Example

Delete the File type from FileMonitor.cass
local($result) = $Connect->Get($server_URL,
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "Operation=RemoveType&".
                               "Type=File&");
if ($result =~ /^0:/)
   print("Type removed");
} else {
   print("Error:$result");
}

ListEvents

ParameterDescription
Operation ListEvents
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Type Object type from which we want a list of all possible events.
Table 10. List events in the specified type
Note that there is no API currently for listing Types. Users find a type that they want to get a list of events for by finding an object of that type in the object hierarchy.

Responses

Example

Print a list of events affecting cassServelet.java
local($result) = $Connect->Get($server_URL,
                               "Operation=ListEvents&".
                                "Account=FileMonitor.cass&".
                              "Type=$ObjectType");
if ($result =~ /^0:/) {
       # If the result is OK, then put each line of the result in a separate element of an array
       local(@results) = split(/\n/, $result);          

       for ($i = 1; $i <= $#results; $i++) {
          local($event,$definition) = split(":", $results[$i]);
          print("Event: $event, Definition: $definition\n");
       }
       return;
} else {
       print("Error getting events: $result\n");
}

Working with Notifications

Sending a Notification

ParameterDescription
Operation Notify
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
ObjectID Unique identifier for the object being monitored and reported upon by this notification.
Propagate Optional If this parameter is used (i.e. if it has any value such as Propagate=1), then the Notification Server will take the notification and not only apply it to the object that it reports upon, but also apply the notification to the parent and all ancestor objects as well. Thus, if there is a notification of change to cass/src/cassServlet.java, and the Propagate parameter is used, then the user will receive the notification if they have subscribed to cass/src/cassServlet.java, cass/src or cass.

In the example below, two notifications are sent when cassServlet.java changes: one to the file object representing cassServlet.java, based upon the events available to file types, and a second sent to the containing folder cass/src with the propagate parameter set so that events available to the folder can be sent to all of the relevant folders in a single call.

Note that if you were to send an event from the list of File events to the file cassServlet.java and set it to propagate, the file event would be propagated to the containing folders which only expect events of the Folder type. While not a critical error, it will disrupt people's ability to subscribe to these events.

Note that there are advantages to not using Propagate and doing this yourself. For example, the affected object can receive a detailed description of the event, the parent object a less detailed, and so on until you reach people who have subscribed to the root of the object hierarchy and will get the most general, least detailed notification. After all, a person subscribing to the root of the hierarchy has less interest in the individual file that was modified, and more interest in staying in touch with what is going on across the system.

Event Optional If the object has a type associated with it, then use Event to specify which Type-Specific EventName describing what occurred to this object

Note that there is currently no type and event checking. You can send a notification of changes to a File with the event field set to "I am a goose", and it will be accepted. The only side-effect of this is that if "I am a goose" is not an event defined for File objects, no one will know to subscribe to it.

GenericEvent Even if Event is used, always specify one of the GenericEvents so that applications that are either to0:Account {TestClass}.Test Registered simple to analyze the Events and type definitions or come designed with knowledge of only one set of types can still have a limited understanding of what happened to the object. The current list of GenericEvents is:
  1. Active: Light is on, application is now running, object has just been created, etc...
  2. Inactive: Light is off, process or information source is terminating, object has been deleted, etc...
  3. Increase: Data has been added to the object, value has increased, etc...
  4. Decrease: Data has been removed from the object, value has decreased, etc...
  5. Change: Complex changes took place that can be represented with increases and decreases.
Person Optional Specifies who made the change. An important part of awareness is knowing who is doing what. This provides the who part.
Place Optional Specifies where the change were made. It may be an office, a building, a city, some identifying information of use to the users of the information source. Being aware of one's environment can involve knowing where things are happening.
Summary A textual summary of changes, designed to fit on a single line, such as an email subject line, a string of text that can scroll, a headline to an article, details that may be revealed when the user points to a representation of an event, etc...
Value A numerical representation of the event, quantifying it in some manner, and usable by awareness tools that deal in numbers, such as graphing tools, tools that utilize intensity (through use of sound, light, vibration, disruption, etc...) to communicate the intensity of the event.
Data Optional & Currently Unimplemented Contains a file that represents the details of the change. This file can be of any type. It can be a text file, an HTML file, a gif, whatever the application chooses to use to communicate changes. Note that the choice of format must use something that the client awareness tools will know how to interpret. Aan illustrator file for example would restrict the types of people who can monitor changes, and eliminates any likelihood that the pager, tickertape, or other UI will be able to directly display it. However, that is why there are other fields -- so that each awareness tool can find information that they can best work with.

Two suggestions on how this data field might be used:

  1. Representation of change. A gif file, viewable by any awareness tool that bothers supporting gifs can be used to present a before and after snapshot of how a document, system, architecture, etc... looked before and after the changes were made.
  2. Systems like portholes which are based upon pictorial information can include the photo as part of its notification.
URL Optional Rather than tying up bandwidth by sending files with the details of the change to the CASSIUS server (currently implemented), one can include a URL to the file containing the details of the change. This URL can be used by awareness tools to either automatically download further information for its representation of the notification, or can be used if the user requests further information. This is optional, but potentially quite valuable to users. News headline on a scrolling tickertape or a page listing news headlines isn't enough information: users need to be able to click on it to see the full article.
FileType Optional, not yet implemented Provide an extension to specify the document type ("txt", "html","doc"...) of the Data field.
Table 11. Notification Description Table

Responses

GenericEvent Must Have Value means that the parameter was specified, but contained the empty string. Note that there is currently no checking on either GenericEvents or Events to insure that the entries are valid entries according to the lists of valid GenericEvents or Events.

Example

cassServlet.java has been modified. We want to send a notification containing events of the File type to the cassServlet.java object, and a notification containing events of the Folder type to all of the parent objects.
local($result) = $Connect->Get($server_URL,
                               "Operation=Notify&".
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "ObjectID=/home/mkantor/src/cassius/cassServlet.java&".
                               "Event=Create&".
                               "GenericEvent=Activate&".
                               "Summary=/home/mkantor/src/cassius/cassServlet.java has been created&".
                               "URL=http://localhost/~mkantor/cass//home/mkantor/src/cassius/cassServlet.java&".
                               "NumericalValue=512&".
                               "Operation=Notify&".
                               "ObjectID=/home/mkantor/cass/src/cassius&".
                               "Event=NewFile&".
                               "GenericEvent=Increase&".
                               "Summary=/home/mkantor/src/cassius/cassServlet.java has been added to this directory tree&".
                               "Propagate=1&".
                               "URL=http://localhost/~mkantor/cass/src/cassius&".
                               "NumericalValue=512");
if ($result eq "0:Notification Accepted\n0:Notification Accepted\n") {
   print("done");
} else {
   print("ERROR: $result\n");
}

Polling for Notifications

ParameterDescription
Operation Poll
Username A string to identify the user, so that the user's subscriptions may be found.
Password Optional, Not Implemented Confirmation of user's identity
Interface Name used to identify the interface or interface type. If all tickertape applications identify themselves as of type "tickertape" then they will all use the same preferences, enabling the user to switch between a variety of tickertape tools, perhaps to access different features, or perhaps because they work under different computing environments.

However a user may want one style of tickertape to get one type of information, and other styles of tickertape a different type of information, which would require that each have a different interface name.

Since Optional Check for all new notifications to arrive since this time in miliseconds after the standard 1970s date used by unix. If not specified, notification server uses the last time this user/interface polled (something it needs to track). When a tool quits, and is restarted 24 hours later, it may not want every event to happen in the last 24 hours (i.e. every light switch, printer error, etc...), and may instead want the last 10 minutes only. Alternately, it may want to get the last week's worth of events to so that its style of awareness and visualization will have some context to work with.
Include Optional, not implemented This parameter is used to determine when the Data field of the notification should be returned with the notification. The format is a comma separated list of file types, including the '*' wildcard. Any file type that matches the types in the Include list is sent with the notification.
Table 12. Polling for new notifications

Responses

Creating Subscriptions

ParameterDescription
Operation Subscribe
Username A string to identify the user, so that the user's subscriptions may be found.
Password Optional, Not Implemented Confirmation of user's identity
Interface Name used to identify the interface or interface type. If all tickertape applications identify themselves as of type "tickertape" then they will all use the same preferences, enabling the user to switch between a variety of tickertape tools, perhaps to access different features, or perhaps because they work under different computing environments.

However a user may want one style of tickertape to get one type of information, and other styles of tickertape a different type of information, which would require that each have a different interface name.

Formula Formula specifying what information is of interest. See below for more information on creating a formula.
Method Optional possible values are:
  1. Append: Add this formula to existing formulas for this user/interface
  2. Replace: Replace existing formulas for this user/interface with this formula
  3. Remove: Delete all subscriptions for this user/interface. Formula parameter has no meaning in this context.
  4. Copy: Copy preferences from a different interface specified by the CopyInterface parameter, to the specified interface. Can only copy preferences a user already has, can not copy another user's preferences. Formula parameter has no meaning in this context.
CopyInterface Required If Method = Copy This parameter specifies which interface to copy subscriptions from.
Table 13. Parameters for an awareness tool to subscribe to an information source

Formulas

This section describes how to create subscription formulas using EBNF notation.

<AccountClass> := string without '.' characters
<AccountName> := string without '.' characters
<AccountPath> := [<AccountClass>.]<AccountName>
<relationship> := "=" | "!=" | ">" | "<" | ">=" | "<=" | "contains"
The contains relationship is a string comparison, all other operations work on both strings and numbers.

<Field> := "ObjectID" | "Event" | "GenericEvent" | "NumericalValue" | "timestamp" | "Summary" | "Person" | "Place" | "ObjectName" | "Description"
Note for <Field> that all but "ObjectName" and "Description" result in searching the notification database for field values matching the specified formula. The last two fields search the object hierarchy for objects whose name or description matches the formula. Using the two in conjunction allows awareness tools to search for all notifications affecting objects that match certain requirements. "ObjectID" exists as entries into both the notification database and the object hierarchy.
<value> := string | integer
<FormulaSubExpr> := <AccountPath>.<field> <relationship> <value>
<Boolean> := "AND" | "OR" | ""
<FormulaExpr> := "["<Boolean><FormulaSubExpr> {"," <FormulaSubExpr>}"]"}
<SubscriptionFormula := "["<Boolean>(<FormulaSubExpr | <FormulaExpr>) {"," <FormulaExpr> | <FormulaSubExpr>)} "]"
Note that if <Boolean> = "", that is equivalent to "OR". Also note that while not properly expressed above, a formula can be of the form [<Boolean>, list-of-expressions] or [list-of-expressions] (assumes an "OR" boolean) and just list-of-expressions which again assumes an "OR".
Formula Example 1
A user wants to be aware of changes to cassServlet.java. This first option monitors for the "Modify" EventName
[AND FileMonitor.cass.ObjectID = /home/users/mkantor/cass/src/cassServlet.java, FileMonitor.cass.Event = Modify]

Or this formula which monitors for all events affecting cassServlet.java
[AND FileMonitor.cass.ObjectID = /home/users/mkantor/cass/src/cassServlet.java]

Which can be simplified to
[FileMonitor.cass.ObjectID = /home/users/mkantor/cass/src/cassServlet.java]

and then

FileMonitor.cass.ObjectID = /home/users/mkantor/cass/src/cassServlet.java

All of these are acceptable formulas, the last three being equivalent and receiving all events that affect cassServlet.java.

Formula Example 2
Monitor the cass/src folder for files being added and removed
[AND FileMonitor.cass.ObjectID = /home/users/mkantor/cass/src, [OR FileMonitor.cass.Event = NewFile, FileMonitor.cass.Event = ChildRemoved]]

Responses

Example

Create a simple subscription. Note that this example is flawed: Passing '=' in a Get call can cause problems due to the way query strings are read by the tomcat server. It is best to replace all '=' in formulas with "%3D" when using HTTP Get protocols..
local($result) = $Connect->Get($server_URL,
                               "Operation=Subscribe&".
                               "Username=testuser&".
                               "Interface=testmonitor&".
                               "Formula=[AND FileMonitor.cass.ObjectID = /home/users/mkantor/cass/src/cassServlet.java,".
                               "FileMonitor.cass.Event = Modify]");
if ($result =~ /^0/) {
   print("Subscription Created");
} else {
   print("ERROR: $result");
}

Listing Subscriptions

This call can be used by awareness tools and users to find out what they have subscribed to and using which awarenes tools. It can also be used by information sources to find out which objects people have subscribed to. It does the latter imperfectly at this time: FileMonitor.cass can ask if anyone has subscribed directly to monitor cassServlet.java, but must make separate requests to find out if users are monitoring cassServlet.java indirectly (such as by subscribing to a parent object, or subscribing to all events of a certain type regardless of the Object affected by the event).
ParameterDescription
Operation ListSubscriptions
Username Optional A string to identify the user, so that the user's subscriptions may be listed
UserPassword Optional, Not Implemented Confirmation of user's identity
Interface Optional The name of the interface we want to see the subscriptions for.
Account Optional, required if ObjectID, Event or GenericEvent are used
Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
Event Optional Find all subscriptions that explicitly monitor for events of this type. Particularly useful in combination with the ObjectID parameter.
GenericEvent Optional Find all subscriptions that explicitly monitor for events of this type. Particularly useful in combination with the ObjectID parameter.
ObjectID Optional Find all subscriptions that explicitly monitor ObjectID
Table 14. List subscription matching a constraint.
It is important to understand that if you use both GenericEvent and Event, these are ORed together (any subscription that is to either the specified event or generic event). However, all other parameters are ANDED together: A specific user AND interface AND subscribed to an ObjectID and Account, AND either the event or generic event specified.

Responses

Example

Determine if anyone is subscribed to be notified of modifications to cassServlet.pl
local($result) = $Connect->Get($server_URL,
                               "Operation=ListSubscriptions&".
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "ObjectID=/home/users/mkantor/cass/src/cassServlet.java&".
                               "Event=Modify");
if ($result =~ /^0:Returning (\d+) Results/) {
   # In Perl, $1 contains the value of (\d+) above -- some number of digits
   local($resultcount) = $1;

   # Read the result string into an array, one line of the response per array element.
   local(@results) = split(/\n/, $result); 

   # Delete the first element of the array which is the 0:Returning x Results
   shift(@results);

   for ($i = 0; $i < $resultcount; $i += 2) {
       local($UserInfo,$Formula) = ($results[$i], $results[$i + 1]);
        $UserInfo =~ /^(.*):(.*)$/;
       
       local($username,$interface) = ($1,$2);
       print("$username is subscribed to cassServlet.java\n");
   }
} else {
   print("ERRROR: $result\n");
}

Purging Notifications

Currently there is no automated technique for determining when notifications have gone out of date and deleting them. One approach under consideration is having part of the Type definition include an indication of how long the events retain their significance before they can be considered too old and can be deleted. For example, an information source that sends out lots of notifications about minor events may want a short life-span for each notification, wheras systems that send out a few notifications about important changes may want the notification to last for months. And defining some Types that are long lasting and some that are short will allow a system to have both short and long term notifications. But until this is implemented, we provide the ClearNotifications API.

It is important to purge notifications occasionally due to the performance hit in searching for matching notifications among tens of thousands of out of date notifications.
ParameterDescription
Operation ClearNotifications
Account Account path; either AccountClass.AccountName (where AccountClass can be any of the account's classes), or if there is no class then just AccountName
Password Password for accessing this account
OlderThan Optional Delete all notifications that are older than this number of hours. If unspecified, deletes all notifications.

Table 15. Remove notifications from an account.

Responses

Example

Delete all notifications older than 30 days.
local($result) = $Connect->Get($server_URL,
                               "Operation=ClearNotifications&".
                               "Account=FileMonitor.cass&".
                               "Password=testpass3&".
                               "OlderThan=".30*24);
if ($result =~ /^0:/) {
   print("Deleted!\n");
} else {
   print("ERROR: $result\n");
}

Utilities

There are two utilities in the testing folder: These two utilties are equivalent except that the perl version assumes that your request is being sent to http://localhost:8080/cassius/cass. As this is a Perl script, this behavior is easy to change. These tools exist to enable you to send requests to the CASSIUS server and observe the responses.

Bugs/TODO

  1. Deleting and recreating an account invalidates all SQL formulas for that account. Currently this results in a failure of the SQL expression, in the future it should detect the failure and recompute the SQL.
  2. Icon in Object browser needs to distinguish between zero children and children not yet loaded.
  3. If no objects in hierarchy, does not unload prior account
  4. Reselecting account should reload objects (or does this already happen?
  5. Creating objects whose parents are not null yet do not exist should raise an error. Confirm that this is the case.
  6. Consider adding the generic event "NoEffect" for events that have no effect upon an object yet are worth sending notifications on.
  7. Enable BiffArray to find sounds/icons regardless of where it is running from