JCosyne Manual
Project:Biophysics 
Classification:User's Manual
Identification:CSL-MAN-03-@@DOC_ID@@
Copyright © 2003 by Cosylab Ltd. All Rights Reserved.

Document History

RevisionDateAuthorSectionModification
1.02003-11-09Gasper Tkacik
allCreated.
1.12003-11-15Gasper Tkacik
4.Added section.
1.22003-11-18Gasper Tkacik
6.Added section.
allTrivial corrections.

Confidentiality

This document is classified as a public document. Redistribution and use, with or without modification, are permitted provided that:

  1. the copyright notice is retained
  2. a reference to the original document is made in case of modifications
  3. this document may not be distributed for profit except as explicitly permitted by valid licenses.

Scope

The intent of this document is to describe how to use JCosyne framework for remote computation.

Audience

This document should be read by people that want to develop new remote tasks running in JCosyne, or people who only want to get familiar with the JCosyne technology. The document also includes a short walkthrough example.

Table of Contents

1. Executive Summary

2. Introduction

2.1. My story

2.2. Overview

2.3. Technologies

2.3.1. Java and JavaBeans

2.3.2. JMX

2.3.3. BeanShell Script

2.4. Code availability

3. Setting up a JCosyne server

3.1. Manual setup

3.1.1. Obtaining JAR files

3.1.2. Creating configuration

3.1.3. Running the server

3.1.4. Conclusion

4. Framework remote tasks

4.1. Log Collector

4.2. Sentinel

4.3. Executor

4.4. Result Repository

5. Creating your own remote tasks

5.1. Factorization Example

5.2. Remote task details

6. Deployment and Usage

6.1. Local setup

6.2. Remote setup

6.3. JCosyne distribution

7. Conclusion

How to Read this Document

This document's meta-information (authors, revision history, table of contents, ...) can be found above. What follows below is the body of the document. The body is composed of several sections, which may be further composed of subsections.

Typographical styles are used to denote entities of different kinds. For a full list of entities and their respective typographic conventions, please refer to the Styles section of the XML Documentation document.

When viewing the document in a non-printed form, it is possible to submit comments regarding a given section to the document's owner. This is achieved by clicking the mail icon next to the section title. For this to work, your mail must be configured to properly handle the mailto URLs.

1. Executive Summary

JCosyne is a Java based framework for developing distributed computational systems. It is based on Java JMX (Java Management Extensions) technology, and defines - similarly to JMX specification - three layers of code. JMX Server, or the agent, is the environment that runs on the remote computer and is capable of hosting RemoteTasks. Remote tasks are JMX compliant MBeans that perform the actual computations (or any other kinds of tasks for that matter). The ancestor of all remote tasks is provided by JCosyne network and contains a list of useful functions:

The user extends this basic RemoteTask entity and implements a small number of methods such as run(), that performs the actual computation, internalInitialize() that prepares data fields before the compuation and so on. In addition to the JMX Server Agent and the root RemoteTask, JCosyne comes with several predefined tasks that take care of framework functionality:

Finally, JCosyne also comes with support for creating clients: JMXClient is a root class of all clients, and it allows the connection to the remote task to be made in one line of code. JMXAdministratorClient is a GUI tool that allows inspection of the running server, its user defined remote tasks and predefined tasks (such as browsing the repository, viewing the executing processes and logs and so on) and monitoring of RemoteTask attributes and results in real time. DirectAccess is a subclass of JMXClient that allows TWO JMXServers to run independently, one on a remote machine and one on a local machine. The user can run his/her tasks remotely, but at any time clone the whole task to the local machine and play with intermediate results while the calculation continues on the remote host. Or, the user can run the whole system locally, move entries from remote result repository to local one and so on. This class is designed to make it easy to invoke methods from Mathematica using J/Link.

JCosyne relies, apart from Java, on the following additional software:

A word of summary: JCosyne is small and easy to use. The work required to implement a new remote task is minimal and the design patterns are similar to those required by implementing new Ant build task (i.e. define a MBean, its properties with getter and setter, and two or three abstract methods). The ability to seamlessly switch from local to remote mode and the organization of result repository are AFAIK innovative features of JCosyne.

The author is using JCosyne in biophysics computations as part of his graduate study.

2. Introduction

2.1. My story

I have developed JCosyne primarily for my own research work, which encompasses data analysis and theoretical calculations in the field of physics / biophysics. Having experience with computational packages such as Mathematica or Matlab, and at the same time doing serious C / C++ coding, I have always tried to find a best-of-both-worlds solution. In other words, I was looking for a tool that would retain the benefits of "real", object-oriented programming languages (data encapsulation, speed, scalability to complex problems) and the associated support machinery (version management, documentation) with the ease-of-use and visualization of integrated packages. At the same time, I needed a way to run my calculations on a faster, remote computer. Experience has taught me that getting a peek at preliminary results can save hours of waiting, if that peek shows you that you have entered some parameter incorrectly. Or, to give another example: result repository, one of the JCosyne components, evolved because of all the trouble I have been going through maintaining my input files in correct directories, my output files properly annotated and backed up and my parameters well documented, so that I could see after one month what it was that I was trying to compute.

Java and the associated JMX technology enabled the developers to seamlessly run JavaBeans either locally or in remote servers. Having a considerable amount of experience with Java programming, I decided to implement a computation framework in Java: JCosyne. I am sacrificing probably around 10 percent of speed as compared to C and the availability of good numerics libraries (which I hope will slowly appear for Java as well). On the other hand, I am sure we are gaining a tremendous amount of saved time because Java is safer to use, easier to learn, install and deploy, and because it has an excellent standard library. In addition, Java integrates really well with other packages, and I envision JCosyne as a sort of value-added framework: use and integrate what is out there. For instance, by using J/Link from Wolfram Research, it is trivial to get results from JCosyne algorithms into Mathematica during run-time. Most probably similar things can be done with Matlab. JCosyne does, however, provide a small set of administrative tools to manage and inspect currently running processes.

I have decided to make JCosyne public and free. I would be glad to hear any comments, bug reports and feature requests. Even better, if you would like to contribute to JCosyne development, we can always use your help. If interested then, read ahead to learn about how to use JCosyne and develop simple algorithms for it.

2.2. Overview

In JCosyne, you launch a server process on a remote or local machine (or both). A server is simply a Java executable, which starts up, parses some configuration, installs framework remote tasks (to be discussed later) and sits there until you give it something to compute. After the server is up-and-running, you can install into it your remote tasks. These are Java classes that implement a certain predefined interface and encode the algorithm you would like to execute. There are various ways of installing a remote task into the server. You can write a Java client that does it, or you can do it through J/Link interface, or perhaps interactively, through some GUI.

After the task is installed (which basically means that the server has loaded the Java class and instantiated it), you provide the task with input data and start it. It executes in the server, sending out notifications about the progress, optionally also intermediate results and log entries, which you can interactively view on your client machine. When finished, the remote task produces a record of its execution and remains in the server, ready to execute any further commands you may want to submit to it interactively.

Figure 1: The figure shows the principal components of any JCosyne installation. JMX JCosyne servers can run either remotely, locally (or both). Every server runs a suite of framework services by default, and also all remote tasks you decide to install into it. The remote tasks and services may then be accessed from different environments. As examples, we show the JMXAdministrator GUI, which is part of JCosyne framework, in the upper-left corner; Mathematica of Wolfram Research using J/Link in the upper-right and Eclipse as a Java IDE as the third possibility.

All modes of interaction usually work best combined. You develop and test your algorithms - remote tasks - in a Java IDE, and use a build script to send them to the server machine. You then write Java clients or use interactive environment such as Mathematica to install, set up and start the tasks and analyze the results. During the whole period you may want to use JMXAdministrator GUI to follow the computation and keep track of the server status.

This document is structured as follows. The next section will focus on how to set up a JMX JCosyne server. Later, we discuss the functionality of the framework remote tasks, which come as part of every JCosyne deployment and run in every server. After you get familiar with the environment in which your remote tasks can run, we will develop a factorization remote task together. Finally, the last chapter shows how to write a client and how to use tools and libraries to interact with the server and running remote tasks.

2.3. Technologies

In this section I will try to write a paragraph or two about the technologies on which JCosyne is based. You may safely skip this section, it is indended for people that are interested to see how JCosyne works, and optionally for developers that want a short look into the inner workings of the framework.

2.3.1. Java and JavaBeans

JCosyne is a Java-based framework. Java is a programming language developed by Sun (and now in a open community process style development), which is platform independent, object-oriented and easy to use. While it has certain shortcommings as compared to C (brute-force performance), I believe the benefits far outweigh them. See an excellent Java tutorial page from Sun to learn about how to write Java programs - it's worth it! In addition, writing code in Java allows the development and compilation to be done on one platform, while execution can be done on another.

As already mentioned, Java also offers excellent standard library, with libraries ranging from classical I/O to APIs for image processing. In addition, Java defines a programming model, called JavaBeans. This is a set of design patterns, or formalized agreements, between the language itself and the code developer, that restricts the complete set of all defineable Java classes to a small subset that abide by additional JavaBeans rules. If you follow these rules and write JavaBeans code, automated tools and libraries will make use of your code to provide a lot of added value. In this way JCosyne uses Java Management Extensions (JMX), which is an API and a reference implementation from Sun, which makes your computational tasks remotable (i.e. it gives them the capability of running in a separate process over the internet, and you do not have to do any additional work to support this).

JCosyne needs at least Java 1.3 to run, although Java 1.4 is preferred. Internally, the framework makes use of the new reflective technologies of Java, such as dynamic proxies. The installation procedure takes care that you have the correct version installed.

2.3.2. JMX

A formal specification for JMX is available at Sun's JMX page. It is easily understandable and gives a few examples. Let me, however, just briefly define the terms that I will use in this document here. By agent or, synonimously, JMX server I mean the process that runs on a remote machine, and runs both the framework and your remote tasks. A JMX bean (MBean for short), as defined in a specification, is a JavaBean which follows some additional, simple rules. You will learn them as we go along, because all your remote tasks will be delivered in a form of JMX Beans. A remote task, or algorithm, is therefore a JMX MBean, that is a Java class following certain additional rules.

To give you a taste of what MBeans do, let me enumerate some of their basic capabilities. MBeans expose methods, attributes and notifications to the outside world. Attributes are like JavaBeans properties: these are named and typed data items, that can be read (and optionally set) through methods with special signatures. For example, as soon as a MBean declares a pair of methods of the form

public void setLongNumber(long num);
public long getLongNumber();
					

the JMX architecture knows that the MBean has an attribute of type long, with name LongNumber (notice capitalization, different from JavaBeans), which is both readable and writable. Furthermore, you should think about the pair of methods (they are called accessor and mutator) as doing precisely that, without any additional side-effect. Then you may think of defining the state of your MBean as the set of all writable attributes. Why? Because that is the only way in which you can set the internal values of your MBean. You usually instantiate it with default constructor, call a number of mutators that set the correct internal state, and then invoke methods that operate on the internal data and cause state transitions in your MBean. MBean can then announce these changes by emitting notifications, which are extended JavaBeans events. All of them extend one Notification class, defined by JMX. All interesting parties register as notification listeners to the MBean and optionally provide a filter that determines exactly which notifications they are interested in. The JMX framework then makes it easy for your MBean to dispatch these notifications to all listeners. A special type of notification is of note here, and that is the attribute change notification, which the MBean may emit to inform the listeners that a certain attribute value has changed.

Figure 2: Remote task MBean (algorithm for factorization) exposes a read-write attribute LongNumber which the user sets prior to the start of execution (setting initial state). The client then registers as attribute change listener to monitor the growing list of factors, represented by a read-only attribute. After everything is set up, the execution starts by invoking run method on remote task. JMX over RMI makes it seem as if the client were interacting with a normal, local Java class.

When you develop JCosyne client or remote task, you will not see any of the JMX technology up-front. You should realize, however, that when you call a method on your task in a client, and that method executes in the server, it is the JMX technology (over RMI) that makes it sure to keep the remoting process hidden from you. The only location where you, as a user, come into contact with JMX details, is when we discuss configuration. You naturally have to provide a server host and port number, and specify certain paths for server and client data files.

2.3.3. BeanShell Script

When you develop your Java algorithms as remote tasks, you declare interfaces with methods that expose the results of your computation and let you modify the state of the algorithms after they have been installed on the server. However, changing that interface involves recompiling the code, distributing it to the server, and most importnatly, restarting the server. Sometimes you would just like to peek into the instance of your remote task, tweak some internal parameter or inspect it. Sometimes you would also like a certain Java code fragment to be executed right before the calculation starts or after it ends. JCosyne uses BeanShell Script for this purpose. BeanShell Script is a Java scripting language that allows you to basically download a fragment of Java code as a string into your process, and execute it there immediately. It can access all, including private fields, of the instance in which it is run. JCosyne supports two ways of executing bean script. First, for each task, you can define - as part of task's state - scripts that are connected to state transitions (to be discussed later). So whenever a remote task progresses from one internal state to the next (i.e. after it has been initialized and before it starts execution), a script can be executed. This script would usually store some state to the file, or modify internal state in some way, or maybe just print a debug output. Similarly, there is a possibility to install a script that will execute after the computation finishes. And secondly, you always have the option of executing a script right here and now, even if that is in the middle of computation. However, JCosyne will suspend computation before the execution and resume it afterwards, to keep the internal state of the algorithm consistent. Note that since the script is just a string segment of Java code, you can send scripts to your algorithms even from Mathematica, for example. Go to BeanShell homepage for more info.

2.4. Code availability

I decided to make the code publicly accessible under GPL license (see license details). The Java code for JCosyne is prefixed with com.cosylab.jcosyne to reflect the fact that it was developed as new technology evaluation of JMX which I was doing for Cosylab, a high-tech software company in Slovenia. While remaining in GPL, Cosylab might also decide to support JCosyne in the future. The specific computational tasks that use JCosyne need to be GPL, but should not be placed in the com.cosylab namespace. For example, my biophysics computational tasks are in edu.princeton.genomics namespace and are also in no way related to Cosylab. The official page of JCosyne is at Sourceforge.

3. Setting up a JCosyne server

3.1. Manual setup

In this section we go through server installation procedure step-by-step. Let me assume you are installing the software on Linux, and have already installed the Java runtime. Then the steps are pretty simple:

  1. Create the directory for the server. Create an empty directory (call it X), for example "/home/user/jcosyne".
  2. Obtain the necessary JAR files. You will need a jcosyne server jar file, jar file with remote tasks that you define, and all 3rd party dependencies (JMX jar files and BeanShell script).
  3. Create server configuration file. The configuration file comes in the form of plain text Java Properties file. We will explain what to put into this file.
  4. Create a batch file for running the server. The batch file should set the classpath by including all necessary jars. For convenience, you may also execute the server in a loop which will allow you to restart it from the client.

3.1.1. Obtaining JAR files

To run JCosyne, put the following JAR files into X:

ProductJAR filenameWeb location
JMX implementation from Sunjmxri.jarJMX Implementation Version 1.2 or 1.2.1
JMX RMI connector from Sunjmxremote.jarJMX Remote Reference Implementation Version 1.0
BeanShell scriptbsh-2.0b1.jarBeanShell Script 2.0 beta 1
JCosyne serverjcosyne.jarJCosyne Sourceforge site
JCosyne remote tasksremotetasks.jarDeveloped by you.

Table 1: JAR files needed to run JCosyne server.

You can most probably use newer versions of the software, if they are available. The JAR file with remote tasks is developed by you and contains your algorithms - we explain later how to make such jar file. However, you do not necessarily need it to run the server or the supplied factorization example, we only include it in the table for completeness.

If you put these files all into directory X, i.e. if the 3rd party libraries and the remote tasks jar file are in the same directory as the jcosyne.jar and if they have the names in the table, you will not need to set the classpath (specified in the jar manifest). If you decide to put some of the files in alternate locations or if they have different names, you will have to declare the classpath, as we describe in the following paragraphs.

3.1.2. Creating configuration

JCosyne server and client have very flexible means of configuration. The configuration always comes as a set of (key, value) pairs, where both key and value are ordinary strings. Java can store sets of such pairs into so called Java Properties file, which is a normal text file. Each line is of the format "key=property" and is terminated by a line separator on that system. Key and property can contain whitespace, see Java Properties javadoc for details.

For the server to start up, it must find at least the following properties set:

Property NameValue rangeDefault valueDescription
hostnameIP number or hostname127.0.0.1Name or IP of the computer where the server will run, can be 127.0.0.1.
ResultRepository.recordingtrue or falsetrueIf true, result repository will store the entry for each algorithm run in the server to permanent storage.
nameany stringJCosyneServerWhen the server starts up, it exports to RMI registry an entry about its existence. Clients need to locate the server with (host, port, name) triplet and must provide the same name as was specified in this configuration.
portany valid port number10050Port on which the server will listen for clients.
ResultRepository.pathany path without leading and trailing path separators (slashes).jcosyne/dataThis is the directory where result repository will store input files, output files and indices for all algorithm runs

Table 2: Property names and values for JCosyne server. Remember to prefix all property names in the table with com.cosylab.jcosyne.server.

To specify the properties listed in the table above, be sure to always prefix them with com.cosylab.jcosyne.server.. For example, to set the hostname, you use the syntax "com.cosylab.jcosyne.server.hostname=...".

To see the details for these properties, read javadoc on com.cosylab.jcosyne.server.Constants class.

If you want the server to set an arbitrary system property with key key to a certain value value, you can put the following entry into the server configuration file:
system.<key>=<value>
						
When the server starts up, it will examine its configuration for all property keys that start with "system." (this prefix is set in the com.cosylab.jcosyne.server.Constants interface); for every such property found, it will strip away the prefix and set a system property with the remaining portion of the key to a given value. This is useful if the server uses 3rd party libraries that require some system properties to be set.

A few clarifications are in order. Firstly, result repository is one of the framework remote tasks (class com.cosylab.jcosyne.server.ResultRepository) that is automatically installed into every JCosyne server. We will have much to say about it - for now, it should suffice to know that this class will manage all input and output files for your computation, and that it will store indices for every algorithm run that the server starts. Secondly, the defaults described in the table will be used if you do not specify the value explicitly. JCosyne (both client and server) use the following method of getting a property value, in a hierarchical fashion:

  1. First, they check if Java system property is defined with a given property name, and if so, JCosyne will use this value. You define Java system property from your code with System.setProperty(), or alternatively, you define it by running java interpreter with:
    java -D<property_name>=<property_value>
    							
  2. If not found in system properties, JCosyne checks if a special system property com.cosylab.jcosyne.server.config is defined (as above). This property should contain, as a value, an absolute filename of a Java Properties file, from which other properties are read.
  3. If such property is not set, JCosyne opens <home>.jcosyne/Config/server.props directory, where home stands for your user home directory (queried by System.getProperty("user.home")). If this Java Properties file is found, it is parsed for property name-value pairs.
  4. Finally, if a given property still cannot be found, a default from the table is used. These defaults are stored in the jcosyne.jar file, in Config/server.props location inside jar.

You see that you can define properties in many ways; those in the table above with a lower number take precedence over those with higher, if the property is defined in two places. We suggest that if defaults work for you, you do nothing. If not, use the method whereby you create .jcosyne/Config directory and place server.props file into it. You should treat other methods as advanced and use them if you are sure what you want to achieve.

com.cosylab.jcosyne.server.name=JCosyneServer
com.cosylab.jcosyne.server.port=10051
com.cosylab.jcosyne.server.ResultRepository.path=bioserver/data
					
				

Listing 1: A sample server properties file, specifying servername, port and repository location, and using defaults for other properties.

3.1.3. Running the server

We suggest you create a script for running the server. The script should set the classpath (if needed), and should run the server in a loop. Let's go through an example first, which we comment on later.

					  
#!/bin/bash
while [ 1 = 1 ]
do
/usr/java/j2re1.4.1_01/bin/java -jar jcosyne.jar
if [ $? = 130 ]
then exit 0
fi
done
					  
					

Listing 2: Bash script for running JCosyne server on Linux.

Notice the following things about the script:

What is the purpose of this loop? Well, there is a way to request the server to shut down from the client, by using the sentinel framework remote task, which runs in every server. If this is invoked, the server simply exits (with exit code 0 if everything works and exit code 100 if there was a shutdown error). If we run the server from a similar batch file, the batch file will then restart the server. This is very useful when we develop code, or new remote tasks: the server runs on the remote machine, we create new jars, upload them to the machine (possibly using automated Ant tool, explained later) and from JMXAdministrator GUI panel just restart the server. Such an action will reload all clases from newly delivered jars and will make sure that there is no confusion between old and new code.

If you do not need such reload functionality, the batch file and the endless loop are of course not needed. For Windows based systems you should adapt the batch accordingly.

The above batch file is distributed along with the JCosyne framework and is ready to be used. It is called jmxserver.

3.1.4. Conclusion

These steps should explain clearly enough how a JCosyne server is installed. You must make sure that the port that the server is being run on is open, otherwise you will get strange exception errors. The server will display a series of messages when it starts up, and will continue outputting logs of remote tasks, when they are executed. The last server startup message should be "Done, waiting for connections...". If there are any problems, let us know on Sourceforge. An automatic JNLP installer version is also planned for the server.

4. Framework remote tasks

This section describes remote tasks that will be installed automatically when you run the JCosyne server. If you look carefully into the output produced by the server that is starting up, as we have described in the previous section, you may notice messages such as:

 
 Start LogCollector MBean
 Initializing LogCollector...
 Start executor MBean
 Nov 14, 2003 7:07:37 PM com.cosylab.jcosyne.server.LogCollector$ServerListener handleNotification
 INFO: Registering LogCollector with 'MBeans:type=com.cosylab.jcosyne.server.Executor'.
 Initializing executor...
 Start sentinel MBean
 Nov 14, 2003 7:07:37 PM com.cosylab.jcosyne.server.LogCollector$ServerListener handleNotification
 INFO: Registering LogCollector with 'MBeans:type=com.cosylab.jcosyne.server.Sentinel'.
 Initializing sentinel...
 Start result repository MBean
 Nov 14, 2003 7:07:37 PM com.cosylab.jcosyne.server.LogCollector$ServerListener handleNotification
 INFO: Registering LogCollector with 'MBeans:type=com.cosylab.jcosyne.server.ResultRepository'.
 Initializing repository...
 Setting result repository root path to 'bioserver/data'.
 Done, waiting for connections...
  

Listing 3: Sample server output during startup.

The messages given as an example report about the execution of four very important framework remote tasks, namely LogCollector, Executor, Sentinel and ResultRepository. The following table gives a brief description of each component, and more thourough descriptions follow.

NameClassDescription
Log collectorcom.cosylab.jcosyne.server.LogCollectorThe log collector monitors JCosyne server for all remote tasks that get installed into it. For each task, it registers as a log notification listener on that task. It therefore collects log notifications from all tasks and emits them forward. In this way each client can register as a log listener only with log collector, which will take care that logs from all components reach the client. Log client keeps track of all past log notifications, as well, so that newly connected clients can obtain old server logs.
Sentinelcom.cosylab.jcosyne.server.SentinelSentinel is a very simple remote task that waits to have its shutdownServer() method invoked. As soon as this method gets called, it terminates all other remote tasks gracefully and dispatches shutdown notification. This notification is caught by JCosyne JMX server itself, which terminates in response.
Executorcom.cosylab.jcosyne.server.ExecutorExecutor keeps a list of all currently running (i.e. active) remote tasks. It manages their threads. You start, stop, resume or suspend a given remote task by issuing a request to the executor. Executor also registers as execution notification listener to all remote tasks so that it can update its list of currently active tasks.
Result repositorycom.cosylab.jcosyne.ResultRepositoryThis is the most complex framework remote task. It maintains a database on persistent storage (disk) of all input files, output files and all remote task execution runs. For example, if you run a factorization remote task, the result repository will register to it as an execution notification listener. As soon as the factorization task starts, result repository will capture its initial state. When the task completes, the repository captures the final state. It also keeps track of whether your factorization requested any input files and / or whether it has produced any output files. When the task completes, result repository writes all these summaries to persistent storage. It contains methods to browse through these arhchives (given some criteria), or even to use the records to recreate a remote task from persistent storage back into the server.

Table 3: Framework remote tasks present in every JCosyne server.

For details on each of these framework remote tasks, please see javadoc. In the paragraphs that follow I will try to diagrammatically present objects and actors involved in interaction with these tasks. Hopefully this will make navigating the class hierarchy easier.

4.1. Log Collector

This simple framework remote task exposes to the users only two features. First, it serves as a source of logging notifications. A logging notification is an instance of javax.management.notification, which is of type com.cosylab.jcosyne.server.Constants.MESSAGE_LOG. This notification carries, as its user data object, an instance of java.util.logging.LogRecord. In fact, JCosyne supports logging in a very intuitive way. All remote tasks, the server and the client can use instances of loggers that are already prepared for you in remote task, server or client base class that you will extend. These loggers intercept all logs that you instantiate, pack them into notifications and emit them to the listeners. Since log collector is always a listener, it will collect all log notifications from every remote task. Log collector then, for each notification received, emits a copy notification to its listeners. In order to receive JCosyne logs, therefore, register as a notification listener on the log collector.

The second feature is the ability to receive old log records. Log collector keeps the records from the point at which the server was started. Even if the client only connects after 2 days, it can still receive all log entries by invoking getOldLogEvents() on the log collector. This method returns an array of log notifications.

To make it easier to picture the role of log collector, we provide the following diagram:

Figure 3: Colored components are parts of JCosyne logging. Remote tasks interact with normal Java logging system, which will automatically emit JMX notifications. Log collector will listen and multiplex all such notifications, and will store them for future reference. The client, apart from managing its own logs with Java 1.4 logging, also receives remote logs from the log collector. These logs then carry a special flag and source attribute, which both indicate that they originate in the remote layer.

4.2. Sentinel

Sentinel exposes only two features, in a similar manner to the log collector. The first is a method called shutdownServer(). A client (see figure) can call this method if it wants the server to shutdown. The sentinel will, in response, first use the Executor remote task (discussed below) to terminate all of your remote tasks, then it will explicitly terminate framework remote tasks (log collector, executor itself and result repository), and after that it will send a shutdown notification. This notification is a javax.management.Notification instance carrying com.cosylab.jcosyne.server.Constants.SERVER_SHUTDOWN message type. Each instance of JCosyne JMX Server listens to these notifications: once caught, the server will release all resources and exit Java virtual machine.

Figure 4: Sentinel operation. In addition, arrows surrounding the server process demonstrate the chain of events when the server is executed from a script that runs the server in a loop. When sentinel terminates remote tasks and shuts the server down, the server exits JVM and the script restarts it, reloading all classes in the process (so that any changes to the JCosyne or remote tasks take effect).

4.3. Executor

Once the remote task is installed into the server, Executor framework remote task is your gateway to it. Through the executor you control the behavior of the thread in which your remote task runs: you invoke methods on the executor which start or stop, resume or suspend the thread that is running your remote task. The figure below shows two main branches of functionality where the user interacts with the executor: the first is the control branch, with four thread-controlling methods. The second branch is the query branch, allowing you to learn about all remote tasks currently running on a JCosyne server (server is the process, which contains both the executor and remote task in the figure).

Figure 5: Interaction between the user and the executor. A list of active tasks is maintained as the executor's internal state and is transient (i.e. lost if the executor itself terminates).

The interaction between the executor and other remote tasks has been simplified in the diagram: the only arrow between the executor and the remote task says "Controls". The control is implemented in a two-step process. When the executor requests that remote task, for example, starts the execution or suspends, it firstly sets some variable on the remote task, and secondly waits for the confirmation that the state transition has really occured. The confirmation is carried out by the remote task, when it dispatches execution notifications. An execution notification is actually a subtype of javax.management.Notification called com.cosylab.jcosyne.server.ExecutionNotification. After the notification has been received, the executor updates its list of active tasks.

The reader may ask why the change in state is implemented as a two way process. After all, if the executor creates a thread in which the remote task runs, it is free to terminate it, or suspend it. However, following new Java guidelines where such operations on threads are deprecated, the executor only notifies the remote task of what it should do, and it then manages its own thread, which is allowed and keeps the state of the remote task consistent. For example, if you want to suspend a calculation, you invoke suspendRemoteTask() on the executor. In turn, the executor will set Suspended attribute to true on the remote task. Now it is the duty of the remote task to periodically examine its Suspended attribute, and if it detects that it has been set to true, it can block in the correct place. "In the correct place" is a crucial term here: in the middle of the calculation, we do not want the remote task to stop just whereever, but at some place where all temporary variables, memory allocations, temporary results etc are "consistent". This is important especially if the user then decides to invoke some query getters on the remote task, while it is suspended. We demand, that in such cases (which are frequent in practice - you suspend the calculation to look at preliminary results) - the remote task to be consistent. If the executor would suspend the thread by itself, it would leave the remote task in an undefined state. On the other hand, such suspension mechanism requires cooperation from the remote task - a misbehaved task which never checks its Suspended attribute will simply not be suspended. JCosyne framework offers tools that make such checking in your remote tasks trivial.

To see the specifics of executor interacting with the remote tasks, take a look at executor javadoc. Basically, executor controls or monitors Running, Initialized, ReadyToRun, Finished and Suspended attributes, which every remote task has. These attributes define the so-called lifecycle of the remote task. A combination of their values uniquely identifies what, at that instant in time, a remote task can and cannot do. Moreover, from a state determined by these values, transitions in only some of the other states are possible. For example, if a remote task is in Finished state, it cannot make a transition to any other state.

4.4. Result Repository

Result repository interacts with both the user (for example in the form of GUI JMXAdministrator application) and the remote tasks. The following figure describes the range of interactions; for details, it is best to see javadoc.

Figure 6: Interactions of result repository framework task with the client and with other remote tasks.

If we focus on the interaction of the repository with other remote tasks first, we can concisely describe it in this way.

  1. Whenever a new remote task is installed in the server, the repository registers to it as an execution listener. Let's for example say that you have installed our example factorization task.
  2. After you install factorization task, you set its state - in this case, you set LongNumber attribute to the number that you would like to factorize. Since this is a read-write (RW) property, it counts as remote task state for JCosyne framework. Just before it starts executing, the factorization task emits execution notification.
  3. If you take a look at ExecutionNotification class in com.cosylab.jcosyne.server package, you will see that it carries the remote task state, as a list of all RW attributes. Result repository catches this notification and creates a new instance of RemoteTaskInvocation. This is an object into which the repository records the initial state, the name of the remote task, the timestamp and some other details.
  4. If, during execution, our factorization task needed some input or output files, it would not open them directly through Java I/O. Rather, it would ask result repository to open them in its name. This procedure has three benefits: first, the result repository knows where all files are located (and this is settable as discussed in Server configuration section); therefore, the remote tasks themselves do not need to hardcode the absolute paths of their resources. As it is shown in the figure, the repository is physically located on permanent storage, in the path identified by the concatenation of user's home directory and the repository path, which is read from configuration. If you decide to move the whole repository, no other change is needed other than update to the pointer to repository path. The second advantage of accessing I/O files through repository is that they are accessed as streams only; in reality therefore the input files could even reside on a separate computer and be accessed, for example, over the web. The third advantage is that result repository automatically records every access to an input resource and every produced output resource into the remote task invocation instance. See com.cosylab.jcosyne.server.RemoteTaskInvocation javadoc for details. Remote task invocation therefore contains a full account of where the input state of the remote task can be reconstructed from (i.e. from RW attributes plus input files), and where the results are to be found.
  5. When the factorization task compeltes, it emits the stop execution notification. This notification again carries remote task state, this time the final state. Result repository catches this notification and transfers final state from it into the remote task invocation instance. It then opens the repository and serializes the remote task invocation object into the index folder there.

What about the interaction of the repository with the user? Here the important thing is that when the result repository starts up, it parses its index directory and from it creates a list of all remote task invocation records. The user can now query (see figure), based on some criteria, all records for all prior algorithm executions. The user can also modify these records by using deleteRecord() or storeRecord() repository methods. An example is when the user annotates a record in the JMXAdministrator GUI by adding a string comment to the result. After such annotation, JMXAdministrator calls storeRecord() to save the annotation to the permanenet storage. The repository also supports a simple version of binary transfer protocol, where the user can download an entire input or output file by calling appropriate methods.

See server configuration section (3.1.2.) for configuration properties that affect the functioning of result repository (look for property names starting with ResultRepository).

This concludes our discussion of framework remote tasks. Javadoc is pretty extensive for these tasks and will explain how to utilize the resources offered by JCosyne framework. If you read through this section, you may be perplexed by the complexity of the API. You should not worry - when you create your own remote tasks in the next section, you will see that most of the interaction with these remote tasks is conveniently hidden by default abstract implementation of RemoteTask.

5. Creating your own remote tasks

5.1. Factorization Example

This subsection provides a brief overview of the steps necessary to create a new remote task. Some of these steps are required because you must comply with JMX design patterns for MBeans, as discussed in the JMX technology section (2.3.2.) . There are steps which you have to follow to create a Java class that incorporates into JCosyne framework itself. Finally, in the end you have to package your code and deliver it to the JCosyne server, where it will be executed. For those who like UML diagrams, the following scheme may clarify the dependencies that are explained later in the text.

Figure 7: Relationship between Factorization implementation and FactorizationMBean interface, and its superclasses / superinterfaces.

The following is a list of rules that pertain to how you organize your code for a remote task. They presuppose some introductory level of Java programming language.

Let's finally implement the run() method. To review our progress: we have developed the MBean interface and we have implemented all required methods except for run(). As far as execution goes, this means that our factorization remote task, up to this point, can:

It may seem confusing at this point because we are implementing methods for which we do not know when they are going to be executed, because the framework executes them. The timing details and state transitions will be explained in the next section. However, you should get used to this kind of thinking, since this is the way in which frameworks operate: you implement a number of methods and leave the execution sequence to the framework. For you, right now, it suffices to know what each method has to do, and the framework will then orchestrate the complete execution.

Getting back to the run() method, I provide the listing below:

			  
public void run()
{
	notifyExecutionStarts();

	try
	{
		long total = (long)Math.sqrt(number);
		long ts = System.currentTimeMillis();
		for (long i = 2; i <= total; i++)
		{
			checkState();
			long time = System.currentTimeMillis();
			{
				if (time - ts > 2000)
				{
					ts = time;
					setProgress(1.0 * i / total);
				}
			}
			if (number % i == 0) 
			{
				factorList.add(new Long(i));
				factors = new long[factorList.size()];
				for (int ii = 0; ii < factors.length; ii++) factors[ii] = ((Long)factorList.get(ii)).longValue();
				notifyAttributeChange("Factors", null, factors);
			}
				// just to make everything slower
			try
			{
				Thread.sleep(10);
			} catch (Exception e) {}
		}
	} catch (InterruptedException ie)
	{
		if (isAborting()) return;
		notifyExecutionStops("Stopped by external agent.", ExecutionNotification.SUCCESS_STOPPED);
		return;
	}
	
	notifyExecutionStops("OK.", ExecutionNotification.SUCCESS_NORMAL_STOP);
}
			  
			

Listing 6: Listing of the run() method for the factorization example.

Stepping through the code line-by-line, notice the following important things:

This is all we have to do to make our factorization work. The remaining steps involve compiling the source code, packing it into remote task jar file, putting it to server and restarting the server (perhaps with the sentinel) so that it can reload the new task. Do not worry if these deployment steps seem a bit unclear, we will get to them in the concluding section of this document.

5.2. Remote task details

In this section we go into more detailed description of functionality of remote tasks. We focus on the sequence of events that happens, starting with the installation of a remote task. At each step, we point out what we have to do and contrast that with what we optionally can do.

For the first time, we also include the client in our discussion. As an example, we will use com.cosylab.jcosyne.client.DirectAccess to make a client for our Factorization task. Hopefully this will put the things, especially timing, into correct perspective.

Let me therefore first provide a complete source code for the FactorizationExample executable Java client:

package com.cosylab.jcosyne.client;

import com.cosylab.jcosyne.server.Factorization;
import com.cosylab.jcosyne.server.FactorizationMBean;

public class FactorizationExample
{
	public static void main(String[] args)
	{
		DirectAccess da = DirectAccess.getInstance();
		
		FactorizationMBean fmb = (FactorizationMBean)da.getLocalTask(Factorization.class.getName());
		fmb.setLongNumber(123456787L);
		da.startLocalTask();
		
		JMXAdministrator.main(new String[] { "local" });
	}
}
			  
			

Listing 7: The executable Java class that starts a local JMX JCosyne server, and starts the factorization task.

The client is amazingly simple. We first create an instance of DirectAccess class - see javadoc for details. Basically, direct access serves two purposes: it can be our gateway to the remote server, or, alternatively, it can be our gateway to the local server. We can run both at the same time as well! In case of local server, that we use here, if there is none running, it will be transparently started for you. After instantiating the direct access, therefore, we obtain a reference to our factorization remote task. I hope things are getting clearer now: notice how the method getLocalTask() takes as parameter the name of our implementation class - this is just a string, but what is returned (and that is the object with which the client interacts) is an instance of FactorizationMBean. That is what was ment by saying that only the methods present in the interface will be accessible to the client: there is no way to invoke the methods that exist only in the implementation, but are not declared by the interface.

Method getLocalTask() has, behind the scenes, instantiated the complete JMX JCosyne local server (local because it is collocated on the same machine where we are running the client and, in this case, it is actually running inside of the same Virtual Machine). It has then loaded the factorization remote task, meaning that the server has created an instance of it. The next method sets the LongNumber attribute, as we have discussed previously. Finally startLocalTask() method is a shortcut, that behind the scenes contacts the Executor remote task of the local server, instructing it to start our factorization. The last line simply launches GUI client, so that we can watch in real time how new factors are found.

If the same process were to use the remote server, the only change would be the replacement of "local" with "remote" in method names, i.e. we would invoke getRemoteTask() instead of getLocalTask() and startRemoteTask() instead of startLocalTask(). There would be completely no difference as far as the usage of the remote task is concerned. Of course, in case of remote server, we would have to start one on another machine manually.

In case you wanted to use the task directly and not view it through GUI administrator, you could just as well have called getFactors() on our factorization, or have done something else.

Keeping in mind this hands-on example, what is now happening on the server side?

  1. First, a client, such as DirectAccess must contact the server. There is no point in going into the details about this process: it is well documented in the JMX specification. Classes such as com.cosylab.jcosyne.JMXClient (superclass of all JCosyne clients) or DirectAccess (that controls both local and remote servers at the same time) perform this step for you automatically, based on the client configuration (discussed in the last section).
  2. The clients, when they have their getXXXTask(String classname) method called send the classname to the JMX server (this is still general for all JMX servers, it is not JCosyne specific). JMX server then loads the class with classname name and creates a new instance of it by invoking its public, no-argument constructor. At this point the server also loads the classnameMBean interface and fails if it cannot find it. The JMX architecture uses RMI in a clever way to hide all details about remote communication, so at the client side it looks simply as if we have instantiated our remote task locally.
  3. Already during instantiation, the RemoteTask superclass prepares the terrain for us: it creates the logger and distribution handler that we have discussed, so logging is immediately available. As part of JMX initialization (again, this is JMX specification prescription), the preRegister() method on our RemoteTask is invoked by the server. Our RemoteTask superclass in this method learns who its server is and what its name is.

    Technical notice: from this point on, you, as remote task developer extending RemoteTask class, have the access to two variables, namely name and serverRef. Variable name will contain the name of our remote task as assigned by the server. This name is an instance of javax.management.ObjectName and always has a string form
    MBeans:type=com.cosylab.jcosyne.server.Factorization
    					  
    where equality sign is followed by fully scoped name of the remote task class. You use this name whenever you want to interact with the server (if you need to). The other variable serverRef will contain a reference to the JMX server object that is hosting our remote task. This object, again, is something defined by JMX specification. You can invoke methods on it that allow you to access other remote tasks, install new ones etc - there is a full range of server functions available through the server reference. We suggest, however, that you use those only when you know exactly what you are doing.

    If you decide, for some reason, to override preRegister() method of the RemoteTask in your subclass, you must call the parent version as well, using
    super.preRegister(...);
    					  

    At this point, JMX server itself dispatches special notifications that signal that a new MBean is being installed. This is again in accordance with JMX specification. Several framework remote tasks listen to these events, specifically the Remote Registry and Log Collector. It registers itself as execution listener to our factorization in response. The log collector registers itself as log notification listener in response. You see how the framework remote tasks automatically integrate themselves with the newly installed custom remote task.

    Then JMX server calls postRegister() method on RemoteTask. Again, in your subclass you may override this method, but be sure to call the parent's original as well.

  4. After this "handshaking" has completed, our factorization remote task inertly rests in the server. The user then starts to set RW attributes by invoking mutators. At some point, all necessary mutators have been called, and the user decides to start computation. As previously described, this is achieved by ordering the Executor to start a given remote task, by invoking startRemoteTask(remoteName), on the executor. Remote name is exactly the name as an instance of ObjectName that we have already described. However, the clients that you are going to use hide this interaction with the executor and name passing, and let you just call da.startRemoteTask(), where they will take care of all the details.
  5. When the executor gets the request to start a new remote task, it first checks if this task is already running and returns, if so. Otherwise, it asks the task (factorization in our example), if it is ready to run: it checks the value of ReadyToRun attribute. Now you see who it is that calls our required abstract method - it is executor checking if we are ready. Suppose that we are; then, executor will check, whether factorization has already been initialized: it does that by examining the value of Initialized attribute. If you check carefully you will see that this attribute is defined by RemoteTaskMBean and implemented by RemoteTask. In our scenario, the remote task would return false: after all, the factorization task has just been instantiated and has not yet had its initialize() method called. Learning this, the executor then calls initialize().
  6. Method initialize() starts executing in the RemoteTask superclass. It first creates an out-of-thread attribute change dispatcher (this is a technical detail that improves performance). Then internalInitialize() gets called on the concrete remote task class, i.e. Factorization in our example. Afterwards, after factorization returns (by having created a new ArrayList as you have seen in the code listing), the execution thread continues in initialize() of RemoteTask. There it checks if the user has perhaps set PostInitializationScript attribute after installing the remote task.

    Technical notice about scripts: Post-initialization script is a string in BeanShell script language. Basically, it is a Java code string that will get executed at this point, if it is not null. For example, suppose that after setting the LongNumber attribute in my example client I also set the PostInitializationScript attribute like this:
    fmb.setPostInitializationScript("execute(t) { setAccessibility(true); t.log.info(\"Message from script\");");
    						
    If you read the javadoc for RemoteTask you will see that all scripts have to implement com.cosylab.jcosyne.server.RemoteTaskScript interface which declares just one method
    public Object execute(RemoteTask t);
    
    In my script, I implement this method, as you can see above. First, I do a "magical" invocation setAccessibility(true). This is a technical detail of BeanShell script, that allows me to access, from the script, also private class fields. In the next statement, I access log field of remote task and dispatch a message (I have to escape the quotation marks).
    You may skip this script business and return to it later, if you find yourself needing some functionality like this. The nice thing about it is that PostInitializationScript is simply a RW attribute as any other, and is treated by JCosyne framework as remote task state so it gets stored into the repository. In this way you can always check for past runs what scripts were executed and what they did to the remote task. This is of importance because the scripts may actually modify the data in the remote task (and not be so passive as ours that only submits a log). After optionally invoking a script, the initialize() of RemoteTask changes the state of Initialized to true, and fires attribute change notifications.

  7. The execution flow returns back to the executor. The executor now, knowing that the remote task is ready-to-run and has been initialized, allocates a new thread, adds it to the active tasks list, and in this thread, starts running the run() method of our remote task. If you look at the factorization example, we are now again on the known terrain: we fire start execution notifications and proceed with our algorithm.
  8. Let me at this point describe also the option of using convenience methods in RemoteTask to open input and output files. This is very easy. In order to open an input file, you proceed as follows:
    public void run()
    {
    	...
    	InputStream is = getInputResource("factorization.txt");
    	...
    }
    					
    getInputResource() is a method defined in RemoteTask. For you, it connects to the result repository and requests the resource on your behalf. In response the repository notes that your algorithm needs "factorization.txt" and records this into remote task invocation, so that the fact stays stored in permanent storage. It also opens the file and returns a stream handle. Notice that there is no path prefixing "factorization.txt"; the file is loaded from whatever location is defined in result repository as input folder (see section on Result Repository (4.4.) , especially the diagram; and result repository javadoc). If I prefixed the name with a path, e.g. "custom/factorization.txt", that path would as well be resolved relative to the repository input path. There is another set of functions that simplify resource loading, for example loadMatrix(String): these functions not only return an input stream, but actually already parse the data for you. See RemoteTask javadoc for details. In a similar way you can request an output resource, and you will get output stream reference to which you can store your data. Things proceed in analogous manner: result repository notices that you are producing output, it resolves output name relative to its output directory; you also have at your disposal specialized functions, such as writeMatrix().
  9. During computation, you can expose various intermediate results as read-only attributes and you can emit notifications about their changes, as we have done with Factors in our example. You can have as many of these as you want. In addition, the client can at any time use the scripting mechanism to execute a script in the middle of the execution, using executeScript() method on RemoteTask. This method will first suspend the execution, execute the script, and resume the execution, so that the script operates on a constant (non-changing) state of the remote task. You can always check the results of your script by examining the LastScriptResult attribute of RemoteTask (see javadoc); the changes in this attribute are also announced through attribute change notifications.
  10. While it is running, the remote task's state is Running set to true. This switch-in-state is caused by the executor, when it launces a new thread for our (factorization) task and when we inform it back with start execution notification that we really started doing something. As in the example, we periodically invoke checkState() to detect if we should stop or suspend the execution. Method checkState(), if it detects that Suspended attribute has been switched to true, will block the execution thread until the attribute has not switched back to false. The attribute changes its value (and announces changes through attribute change notifications) because the Executor changes its value in response to the user having called suspendRemoteTask() or the resume equivalent on the executor. When suspend or resume take effect, checkState() method of RemoteTask will also fire suspend / resume execution notifications to configrm that it is really blocking / unblocking. We strongly encourage you to use checkState() implementation to check for stop / abort / suspend / resume transitions, although you are of course free to write your own.
  11. Up to now, we have thus described the following activities you can do with support from RemoteTask while your algorithm is executing: computation, I/O through result repository, checking the state (for resume, suspend, stop, abort), progress change notification (see factorization example), notification of change in arbitrary attribute (see Factors in factorization example), emitting log events, scripting. At some point, your algorithm has finished computing and you have written the result to I/O or at least modified some attributes in response and fired appropriate attribute change notification events. After having done, you use notifyExecutionStops() as we have done in factorization example. This event again proceeds to two listeners by default: to the executor and the result repository. Executor removes your task from the active tasks list and deregisters from it as execution listener - as far as the executor goes, its role has been played for that specific remote task. Remote repository, on the other hand, extracts the final state from the stop execution event, which completes the set of data items, collected together in remote task invocation object. The repository will serialize at this point remote task invocation object to permanent storage, into index subdirectory of result repository location. After this, result repository deregisters as execution listener from your remote task and its role is done.
  12. As the last step in notifyExecutionStops() of RemoteTask, this method will set Finished attribute to true and notify all listeners. State Finished is the last state which the remote task can find itself in, and there is no exit from this state to any other state. The remote task now lives on in the server, ready to return final results if the client invokes some result processing method or accessor. Remote task (e.g. factorization) is removed from the server only if it is "killed", i.e. removed using JMX API methods. More specifically, you can invoke deregisterMBean() on an instance of serverRef, the pointer to the reference, which is present in each RemoteTask, or you can invoke the same method through the server reference available in the JMXClient subclass (by calling getServer()). Killing the remote task will cause all framework remote tasks to finally and unconditionally remove all references from their internal tables: log collector will deregister as log listener from your remote task, for example. If you do not have any specific reasons for killing your remote task (such as releasing the memory), you do not have to do it, because the cleanup is automatic when server shuts down.

6. Deployment and Usage

This section describes possible scenarios in which JCosyne can be used. More specifically, we firstly describe how to run JCosyne in a purely local environment, where both the client and the server run on the same machine. This is the way in which the factorization example is launched by default. Secondly, we focus on the mode where server runs on a remote machine and is accessed from the client through the network. In parallel we also give a brief introduction to "cloning" or moving remote tasks and results from remote server to local server. Finally, we give directions on starting GUI JMXAdministrator client, which contains its own JavaHelp.

6.1. Local setup

Local setup is defined as the setup in which both the JMX server and the client run on the same machine. JCosyne framework makes local deployment trivial in the sense that you do not have to start the server explicitly, if you use DirectAccess client. You have encountered this class in the factorization example main() method. This class serves as a gateway to your remote tasks, whether they are instantiated remotely or locally. Let's look at a part of its public interface:


public class DirectAccess extends JMXClient
{
	public static DirectAccess getInstance() { ... }
	
	public Object getRemoteTask(String taskName) { ... }
	public Object getLocalTask(String taskName) { ... }
	public void startLocalTask() { ... }
	public void stopLocalTask() { ... }
	public void startRemoteTask() { ... }
	public void stopRemoteTask() { ... }
}
			  
			

Listing 8: DirectAccess interface: methods for accessing local and remote tasks.

Notice first that the DirectAccess does not have a public constructor. It is a singleton implementation that is accessed through getInstance() accessor. This makes sure that the local server is started only once and that the data structures do not replicate needlessly. However, it also means that in a single JVM you can designate at most one task at a time as "local task" and one as "remote task". This is not a big problem - when you request a reference to a, say, new local task, using getLocalTask(), DirectAccess sets its internal pointer to the local task that you request. However, your previous local task (if there was one) still runs in the local server, and the next time you request it again through getLocalTask(), you will get a reference to it. Remember, DirectAccess is just a convenience class - you do not need to use it. You can always subclass JMXClient yourself if you need special functionality.

That being said, how does the direct access work? Its startup closely parallels that of the server, as described in the section on server configuration (3.1.2.) . The steps taken in the resolution of properties are the same as those taken by the server: the client examins System properties, then proceeds to those specified in JVM argument, then checks user's home directory and finally falls back to defaults specified in the jar file. The following table shows properties needed by the client:

Property NameValue rangeDefault valueDescription
hostnameIP number or hostname127.0.0.1Name or IP of the computer where the server is running, can be 127.0.0.1; in this case local deployment will be in effect.
nameany stringJCosyneServerWhen the server starts up, it exports to RMI registry an entry about its existence. Clients need to locate the server with (host, port, name) triplet and must provide the same name as was specified in this configuration.
portany valid port number10050Port on which the server will listen for clients.

Table 4: Property names and values for JCosyne clients. Remember to prefix all property names in the table with com.cosylab.jcosyne.server.

The defaults given in the table are declared in the fallback configuration file in the client jar - in case you don't specify the values anywhere, such defaults will be used.

After loading and processing the properties, DirectAccess sits still until you call either getRemoteTask() or getLocalTask(). If you call getRemoteTask(), DirectAccess will use the information from configuration to locate the remote server (using hostname, port, server name triplet), bind to it, and make it load the named remote task. As a result, getRemoteTask() returns a reference of type Object, which you can cast to the proper MBean interface. Similarly, if you invoke getLocalTask(), DirectAccess contacts server running on localhost; if there is no such server, one is created in the same process where direct access runs, as a side-effect. From then on the behavior of remote and local methods is the same.

To reiterate: what is the parameter and what the return value to getXTask(String taskName). The parameter is a fully scoped Java class name of the class implementing the algorithm, for example "com.cosylab.jcosyne.server.Factorization". The return value is an instance of the corresponding MBean interface, through which you can access remotely running task, for example com.cosylab.jcosyne.server.FactorizationMBean. Remember that you cannot cast the return value to a class Factorization type, only to the interface FactorizationMBean type (as we discussed in Factorization task example section (5.1.) , you can access tasks only through their interfaces).

With a single direct access, you are allowed to call both getLocalTask() and getRemoteTask(), in whichever order, with different or same tasks as parameters. Two independent servers will run for those requests (except in the degenerate case where you specify localhost as the remote address).

Figure 8: Simultaneous interaction of direct access with both remote and local JCosyne servers.

You can execute com.cosylab.jcosyne.client.JMXAdministrator class with command line parameter "local" to connect to the local server and see what is currently happening there. This is what FactorizationExample does for illustration. Notice that JMXAdministrator GUI itself uses JMXClient and launches a local server, if it is not already running. Also notice that the JCosyne framework takes care that there is a maximum of one local server running: all client classes, be it JMX Administrator or DirectAccess or JMXClient base class, will first check if a server is already running and use that one, if so.

6.2. Remote setup

There is nothing special with remote setup, except that the server runs on a different machine, in a different JVM. When you develop your remote tasks, you have to make them accessible to the server. The easiest way is to pack them into a jar file, distribute jar file to the server, and restart the server so that it reloads the classes. You can use the sentinel plus batch file mechanism explained in the sentinel section (4.2.) for that purpose. If you put your tasks into "remotetasks.jar" and place the jar into the same path along with "jcosyne.jar", then no additional classpath settings are necessary. If you give the jar file another name, you will have to add that jar to Java classpath, before you run the server. You may also want to write a small ANT build script that compiles, packs and distributes the remote tasks to the server machine for you.

6.3. JCosyne distribution

JCosyne is distributed in a single jar file, jcosyne.jar, that contains server, framework remote tasks, JMX Administrator client and the factorization example tasks. This is an executable jar file, which will by default start the JMX server. The other runnable / important classes are listed in the following table:

Class nameExecutableDescription
com.cosylab.jcosyne.server.JMXServerYes.JCosyne server, no special command line parameters, uses configuration properties (3.1.2.) . Run on remote machine.
com.cosylab.jcosyne.client.JMXAdministratorYes.GUI client that connects to both remote and local servers (default), or, if run with "local" command line parameter, only to the local client. Creates a local client if it does not exist.
com.cosylab.jcosyne.client.FactorizationExampleYes.Factorization example presented in this doucment (5.1.) . Starts also JMX Administrator so that you can monitor execution progress.
com.cosylab.jcosyne.client.DirectAccessNo.Instantiate to access the local and remote tasks from the client.
com.cosylab.jcosyne.client.JMXClientNo.Subclass to create your own clients (advanced).
com.cosylab.jcosyne.server.RemoteTaskNo.Subclass to create your own remote tasks.

Table 5: Distribution executable and other important files.

7. Conclusion

Hopefully you will find this information informative enough to start using JCosyne on your own. This document should be used in conjunction with javadoc for best results. Also, trying out different features of JMX Administrator GUI program may give you insight into how things work; I suggest you launch the demo from JCosyne web page and follow the suggestions about what kinds of things you can try out in the demo. There is also a range of functionality that this manual did not touch, which is the interaction of local and remote servers, i.e. the ways of "cloning" a running remote task into the local server, or moving data between local and remote result repositories. You can find instructions on that in JavaHelp of JMX Administrator GUI.

You may contact the author if you have any additional questions, comments or feature requests.