|
CASSIUS 1.9 Motivations, Installation, and Usage |
|
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/
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.
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.gzYou 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.shWhen you have started the server, change directory into cassius/WEB-INF:
> cd cassius/WEB-INFCreate the CASS database, and optionally a testing database called TESTCASS:
> [path-to-mysql-bin]/mysqladmin create CASS > [path-to-mysql-bin]/mysqladmin create TESTCASSInitialize the databases:
> [path-to-mysql-bin]/mysql -D CASS < InitializeGlobalTables > [path-to-mysql-bin]/mysql -D TESTCASS < InitializeGlobalTablesIf you want to test the installation (recommended), there are two approaches:
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.
> cd ../testing > perl testsuite.pl TEST_INIT.dataExecution 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
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.
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.
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:
> cd ../samples/filemonitorNext, execute the program "index.pl" using the following parameters:
> perl index.pl [directory to monitor] [URL to files] [URL to server] [Optional multiaccount mode]
>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=.....
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.
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.
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.
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.
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.
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.
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.
> 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.txtNext, 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
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...
| WebDAV method | CASSIUS Event | CASSIUS GenericEvent | Description |
|---|---|---|---|
| GET | Get | Activate | The file has been downloaded either to a local file system or into the memory of an application such as emacs |
| PUT | Create | Activate | The 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. |
| PUT | Update | Activate | The 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. |
| DELETE | Delete | Deactivate | The file has been removed from the server. |
| COPY | Copy | Increase | The 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. |
| MOVE | Rename | Change | The filename has been changed, but remains within the same folder. |
| MOVE | Move | Change | The file has been moved to a new folder. |
| LOCK | Lock | Activate | The file has been locked for exclusive write permissions of a single user. |
| UNLOCK | Unlock | Deactivate | The 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 method | CASSIUS Event | CASSIUS GenericEvent | Description |
|---|---|---|---|
| GET | GetChild | Activate | An item in this folder has been downloaded. |
| PUT | FileUpdate | Activate | An item in this folder has been uploaded. |
| PUT | FileAdded | Activate | An file has been uploaded into this folder. |
| PUT | FolderAdded | Activate | An folder has been uploaded into this folder. |
| DELETE | FileRemoved | Deactivate | An file has been removed from this folder. |
| DELETE | FolderRemoved | Deactivate | An folder has been removed from this folder. |
| MOVE | ChildRename | Change | A file or folder within this folder has been renamed. |
| COPY | ChildCopy | Change | A 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.
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>
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
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:
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.
Some quick definitions:
| Username | Identifies 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 |
// 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();
}
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.
}
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";}
}
public Toolkit(String inUsername,
String inServletURL,
int inPollInterval,
Long inSince,
int inPolicy,
int inPolicyComparison,
boolean inDebug)
| inUsername | Username of the person who is using this toolkit to poll for and subscribe to notifications |
| inServletURL | URL to the servlet. Likely value is: http://localhost:8080/cassius/cass but this isn't a very portable setting. |
| inPollInterval | In 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. |
| inSince | A 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. |
| inPolicy | The 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:
|
| inPolicyComparison | Value to associate with the DeletionPolicy |
| inDebug | if true, prints out a little debugging information |
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();
}
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;}
}
}
Operation=Poll&Username=user&Interface=Style1&Operation=Poll&Username=user&Interface=Style2&....
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.
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.
> 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.
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
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...
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);
Operation=NewObject&Account=FileMonitor.cass&Password=testpass3&ObjectID=1&
Operation=NewObject&ObjectID=2
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].
| Parameter | Description |
|---|---|
| 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. |
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");
}
| Parameter | Description |
|---|---|
| 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 |
local($response) = $Connection->Get($servlet_URL,
"Operation=UnRegister&".
"Account=FileMonitor.cass&".
"Password=testpass3&");
unless ($response =~ /^0/) {
print ("FAILED!\n");
}
| Parameter | Description |
|---|---|
| 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. |
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...
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");
}
| Parameter | Description |
|---|---|
| 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. |
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");
}
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.
| Parameter | Description |
|---|---|
| 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. |
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");
}
| Parameter | Description |
|---|---|
| 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. |
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");
}
| Parameter | Description |
|---|---|
| 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 |
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.
| Parameter | Description |
|---|---|
| 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. |
[File]/home/users/mkantor/cass/src:/home/users/mkantor/cass/src/cassServlet.java:cassServlet.javaand if there is no object type, then:
/home/users/mkantor/cass/src:/home/users/mkantor/cass/src/cassServlet.java:cassServlet.javaNote 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.
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");
}
| Parameter | Description |
|---|---|
| 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. |
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");
}
| Parameter | Description |
|---|---|
| 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 |
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");
}
| Parameter | Description |
|---|---|
| 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. |
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");
}
| Parameter | Description |
|---|---|
| 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:
|
| 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:
|
| 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. |
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");
}
| Parameter | Description |
|---|---|
| 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. |
| Parameter | Description |
|---|---|
| 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:
|
| CopyInterface | Required If Method = Copy This parameter specifies which interface to copy subscriptions from. |
<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".
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.
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");
}
| Parameter | Description |
|---|---|
| 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 |
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");
}
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.
| Parameter | Description |
|---|---|
| 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. |
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");
}