Version 0.8.0 / 10/10/02
JASon is a container for services activities. This is a practical way for integrating new functions in a cooperating space. JASon provides a lot of mechanism for helping services to know and to share a working flow. User can use it for stand-alone server, for scheduling task or "simply" for building its own Application Server. JASon supports log, cache, many verbosity level, load balacing between thread or process.
a. Introduction
- JASon
- Service Sample
- Service Description
- Verbose
c. Standard services
- AbstractService
- ThreadableService
- TaskableService
- SocketService
- ProcessableService
- SchedulableService
- Toolkit
a. Introduction
b. Door
c. Load Balancing
d. Score
e. Working space
JASon is the Java Activation Server on. This is a container for managing services. A service is a piece of code that implements the jason.core.Service interface. Theorically, this is a thing that can be started and stopped and that needs a configuration for working propertly. JASon doesn't care about the service role, it cares only to offer the best space for helping the service to achieve its job. The way for exchanging service data or interact with the service container is the service context. Each service is initialized with a service context.
What does JASon mainly offer ?
Before describing what can be a Service, I propose you here to study an old straightforward Service : the admin service located before in the bin/conf/services/admin.xml file.
<!DOCTYPE service SYSTEM "conf/service.dtd"> |
In this example, the admin service is known by the class jason.service.admin.AdminService that implements the jason.core.Service interface.
We define a set of properties like the trace support, an admin account, a password or a port value that will be used by this service for initializing correctly. We specify that this service should be "processable" (supports load balancing between processes) with the process attribute. We include too a politics for verbosity for any trace on the console with '<verbose type="*" enabled="true"/>'. Here it means, that all verbose level message should be supported by the service container.
As JASon supports its own "classLoader", you can specify a class location thanks to the classpath attribute. Here we locate the classes in the classes/classes.jar file. This location is again relativly to the service description location (here bin/conf/services/admin.xml). This classpath can contain a CLASSPATH similar syntax using ';' or ':' for separating each jar/zip or directory.
The problem is now to include this service into JASon. JASon supports many way to integrate services. The first way is to declare this service in the jason.xml main configuration file.
Here we locate this description service in the services/admin.jml relativly to the jason.xml file location (in the bin/conf directory).
... jason.xml previous part |
We override a property too for demonstration which it's the administration port. This first step is good enough but it doesn't look pretty practical to include this admin service as a real plugIn. JASon support a plugIn format extended by '.jas'. Look at this description in the JAS format section.
Another method is the usage of the door mechanism.
A door is a piece of code that user can integrate to JASon for detecting
new service and including it into the JASon container. By default the
default door scans a path and includes any service description in XML
or in JAS. For a flawless usage, you could integrate services from another
JASon server or from Internet (by HTTP). Note that the door system is
the
best suggested way for service intergration because it is tied
to the updating service.
Now we know how to insert a new service, we look a bit more at the Service Definition. This service uses the conf/service.dtd DTD for validating the content.
Attribute | Role |
name | Name of the service |
class | Java class of the service. This class must directly or indirectly implement the jason.core.Service interface |
classpath | Service classpath similar to the CLASSPATH syntax |
verbose | Enable/Disable verbosity |
test | Enable/Disable test for the first request |
thread | Enable/Disable the thread pooling usage |
process | Enable/Disable the load balancing between process. |
persistence | Delete or not the service file after inserting into the JASon container |
start-period | Activate the service every specified minutes. Only for service that extends the jason.core.SchedulableService class |
start-minute | Activate the service at the right time for the specified minutes. Only for service that extends the jason.core.SchedulableService class |
start-hour | Activate the service at the right time for the specified hour. Only for service that extends the jason.core.SchedulableService class |
start-day | Activate the service at the right date for the specified day. Only for service that extends the jason.core.SchedulableService class |
start-month | Activate the service at the right date for the specified month. Only for service that extends the jason.core.SchedulableService class |
Attribute | Role |
type | An integer that indicates a verbose level |
enabled | Enable/Disable the verbose for the tied level |
The service properties are described in the property.dtd. This description follows a part of the XML-RPC specification. So user can define complex properties structure with array and hashtable.
Attribute | Role |
name | Name of the property |
value | Value of the property |
When a property doesn't contain a value attribute, it can contain the following tags :
... Service description |
Bellow, we define a property in a service description file which is an "hashtable" containing for the key test3.1 a string and for the key test3.2 an array. In a Service java code, we will get it thanks to the getProperty method.
Here we describe how to manage verbose. In the Service description, we
show that a verbose tag exists. So the question is how to manage it
for selecting only a verbose at a time ?. If you look at the jason.core.AbstractService
which is a common class for Service, you will see the sendCustomMessage(
int type, String message )
definition. Here, we specify a verbose
type with the first argument and a verbose message witht the last one.
You can use several verbose definitions in your service like that :
... Previous service definition |
Here we enable the verbose type "1" and "3" but not the "2". It means that any verbose with sendCustomMessage( 2, 'message' ) will not be processed by the container.
You can use a generic type '*' for describing all verbose type.
Verbosity is also for usual message : Information, Warning, Error and InnerError.
All this is available respectivly with the methods : sendInformationMessage
,
sendWarningMessage
, sendErrorMessage
, sendInnerError
.
These methods are available from the jason.coreAbstractService
or if you wish only to implement the jason.core.Service service
from the jason.core.ServiceContext.
A good way for integrating service in the container is the usage of the JAS format. This format is simply a jar file that contains XML file describing service and a set of libraries and classes for the service working.
For instance, consider the administration service that needs an admin.jml file for description and a classes.jar file for classes.
>jar cvf admin.jas admin.jml classes
The new admin.jas file can now be integrated in JASon through a door location. A default door is in the bin/conf/door directory. We simply copy the admin.jas in this directory and we restart JASon for taking into consideration this service.
When JASon finds a .jas format, it creates a directory labelled from the file name and uncompresses the file. Then, it searches for all .jml file and run as possible each service.
The usage of JAS is in relation with the default Door. Look at this section for more information on the Door.
Note for the 0.8 version : For practical reason, the previous .xml service format is renamed as .jml for the JASon Markup Language. The content is not altered with the 0.7 version.
At this step, we have seen a short decription of service from the XML point of view. Now, we describe the service reality in a more Java sens.
For building a service, user can extend the jason.core.AbstractService
class that implements the jason.core.Service
interface. He can read a property with the getProperty, getIntProperty,
getBooleanProperty
methods. The getProperty
returns
an Object
that must be casted. So for the last choice, be
careful not to alter the property description without changing the service
code.
Here a sample of service :
/** |
In this service, we don't expect to be run more that once, that why we
specify to inherit from the jason.coreAbstractService.
We send an information to the console with the sendInformationMessage
method. This message will be available too in the bin/log
directory (a little delay may be needed for refreshing).
Note that it is not necessary to override the init, start or stop method for doing a particularing step code. Use rather if you inherit from the AbstractService :
notifyInitReady
when the
ServiceContext is set (after the init
method),notifyStartReady
when the
service is started,notifyStopReady
when the
service is stopped.This last code looks prettily ridiculous, so how to change the behavior and run it in a thread that could send a message every 30s. We will see later that the scheduler is a better candidate for that, but for the moment we concentrate on the AbstractService.
The first thing is to change the parent class AbstractService by
the class ThreadableService.
We specify to run this service as a Thread in the constructor by super(true)
,
so the run
method will now be called by a thread. The
second thing is to specify the loop delay for our thread with the setThreadLoopDelay
method from the ThreadableService.
Not that by default the thread as a loop behavior that launches your
run method, so you needn't to manage a loop yourself.
You can stop this thread by the stop()
method
or by the setThreadLoopMode
method.
Here a sample of service :
/** |
A TaskableService
is a good starting parent class when your service needs to do a short
work regularly. For instance, your service could be an HTTP server, so
it needs to do always the same task which is "get a document and return
it to a client with a Socket". TaskableService is
also a subclass of the ThreadableService,
it adds the method notifyTask
that takes a Task object as parameter.
A Task is a little
interface. It has two methods, one for initializing the task (init
)
and one for starting the task (runTask
). This is very similar
to a thread, however it mustn't be confused, because a task is run only
once.
The Taskable
service is for parallelling running. So it can cost a lot of thread depending
on your multiple instance of Task. JASon integrates a
system for dispatching finely the Tasks to threads. Morever, it is able to
delay a Task in a common
Task queue. This task is dispatched to a special service : the TaskService.
Note that is the setThreadable( false )
method is called,
the task will be runned in the current JASon thread which is very bad
for global performance but you have only one service (??).
For helping you to build a Task, an AbstractTask class
is available.
Here a sample of task :
/** Here a part of the admin service, this is a task for} |
notifyTask
method
from the TaskableService.
SocketService is a subclass of the TaskableService. It concentrates on the Socket management. By default it uses a port property from the JASon service description, listening for an host request.
User must override the method getTaskForSocket
.
It returns an object that supports the Task interface.
Hence when a user sends a request it is directly managed by calling
this method. When user returns a Task, this is dispatched to a thread
pooling manager that can itself sends this task in a processing queue
if no thread are available (with the jason.core.service.TaskService)
Several abstract class are available for Task :
getTaskContext
method for getting the Task
context and finally the initial Service context.getSocket
method. Note that your socket
will be automatically closed at the end of the processing.Here a sample of SocketService from the AdminService implementation
... |
In this sample, the AdminService processes any request from the port property value thanks to the AdminProcessing object. Here the code for this object.
class AdminProcessing extends SocketTask { |
A subclass of the SocketService
is the ProcessableService
for load-balancing an host request between processes. The ProcessableService creates
a copy of the request and dispatches it to another JASon server. For helping
the SocketService
to build this copy, you may have to override the method getHostRequestFromInputStream
.
You can find several examples of ProcessableService with the following services :
A schedulable service is another service type, it can be started at a particular date or period. This last value if defined in the conf/service.dtd DTD with the values
These values can be associated, like "start-minute=0 and start-hour=0"
for starting the service once by day at midnight. As the Schedulable
service class extends the AbstractService,
the only thing to do is to defined the run
method with the
needed task.
Here a sample of schedulable service :
package jason.core.service; |
This code simply showes a "start at " message each time the service is started. For instance, if I specify a start-period of 10, this message will appear every 10 minutes.
JASon provides more Services class for helping you.
Class | Role |
jason.core.service.InnerService | This is used only for inner service. This class notifies the container that this service can be overrided by another service |
jason.core.service.DebugService | This service will print all properties when running. This is useful to debug your property description |
jason.core.service.NopSchedulableService | A minimal schedulable service that shows a date when it is started and stopped |
jason.core.service.NopService | A minimal serivce that shows a date when it is started and stopped |
One way to cooperate with other service is the ServiceType
class, this is defined in each service by the setType
method ( from the AbstractService only, else the
Service interface
support the getType
).
For instance :
setType( new ServiceType( ServiceType.CUSTOM, "hello" ) );
In this case, we define (note this is better to set it in the constructor of your service), a serviceType that comes into the CUSTOM category with a hello type target. It means that your service proposes to manage any requests tied to the "hello" category. Note that a service can support several types at once.
On the contrary, you can find now every services matching this type by
the getServiceForType
method from the ServiceContext
class (accessible in the Service interface).
Here a part of the ServiceContext code to show you how to send a request to a particular service Type
public void sendInformationMessage( String service, String content ) { |
This method will invoke a LogService
type by the fireRequestForType
. A Service request
is wrapped with the ServiceRequest
class.
So, another way to delegate a request is the usage of the fireRequestForType
method in the ServiceContext.
For finding a service, the ServiceContext will
search all service matching the good type, then it will try to manage
to request using the manageRequest
method of each
Service. The first one that
will have a good response will be the used one. A good response is a response
that is not null
and that doesn't throws a ServiceException
.
Another important way to delegate a request is the usage of multiple criteria.
The JASon evaluates each services with a rate mechanism. So
you can select a service for a rate using another argument in the fireRequestForType
.
This argument can be one of the following values :
setPersistent(true)
)init
)start
)stop
)getServiceState
)
... |
cleanWorkingDirectory
method from the ServiceContext
will ignore such file.A domain contains all user services. It is defined in the bin/conf/jason.xml file. This file supports the jason.dtd DTD. A domain has a set of service definition. Each service description is stored in a single XML file or bundled in a large XML file. In the last case, a file has several service definition. The location of each service is done relativly to the jason.xml file. For instance if I write that the location of my service is myService.xml, it means it is stored in the same level than the jason.xml file.
Here a Domain definition example :
<?xml version="1.0"?> |
In this domain definition, we load several services from the services.xml file and the file. The first one contains the definition of the HTTP service and the Servlet engine service. The last one is desribed in the first part of this document.
Note that user can override service properties, this is done above with the services/admin.xml file, we update the port property to a value of 10004.
User can update the log system by defining a class that implements the jason.core.MessageHandler interface. This interface has four methods :
/** This method give the current domain, user can ignore it */ |
User can update it with the messageHandler class. By default we use the jason.core.ConsoleMessageHandler class that prints any log to the console.
User can change the behavior of the door by specifying a class in the door definition that implements the Door interface. This interface will return simply all valid services.
User can for instance load services from an HTTP server, or any suitable protocol....
A domain can contains a non limited number of Door.
How it works ? JASon supports the Load Balancing for all service that extends the jason.core.ProcessableService. When you run several process of JASon, each service try to see if another service uses the current port, if it is true, they try to contact the other service to collaborate. When the attribute 'process' is not equals to the 'false' value, the cooperation is established.
Here the standard JASon trace when you run it once :
Starting JASon x ... |
Here the standard JASon trace when you run it twice :
Starting JASon x ... |
In this second running, each service tries to run on the default port. As this default port is already used by the first JASon instance, they try to find another port by getting a new incremented number. When they find a free port, they contact the original JASon services for cooperation. Cooperation is a way to balance each user request into several process. The original services (from the first instance) maintains a cursor that point to a service in the current process or in another process.
To communicate, service uses a simple protocol that starts with the ASCII code 1, so it is impossible for you to use a protocol with this fact. Note that for the moment, a service waits for the code 1 and a port number for establishing the load Balancing.
For instance the crusader module will balance user request to the port 8080 or the port 8081. In the same way, the http service will balance a user request to the port 10001 or the port 10002.
If you stop a service or if a service crashes, JASon will remove this service from the collaborative list and will continue to work with remain services.
Note that in this version, JASon doesn't support to load balancing into serveral computer. This function will be established very soon through an extension of the domain definition
From the API point of view, you have to inherit from the to support load balancing. When a request must be load balanced, it is necessary to download the full request in memory. The problem is that sometime the connexion is never closed depending on the protocol (like HTTP) so you have to show to the service how to be sure to have the full request.
Here the default code from JASon to download fully the user request. This code is available on the jason.core.HTTPService.
protected byte[] getHostRequestFromInputStream( InputStream in ) throws IOException { |
As you can see, this is a simple way to download fully an HTTP request, the request is stored in a byte array only if this is the end of the input stream (value = -1 ) or if two consecutive caracters 10 and 13 (\n et \r) are found.
By default a Service has a score of '0.0' meansing for the first a 0 rate for user request processing and for the last a 0 rate for service request. Here a sample of JASon scoring.
Starting JASon x ... |
After each Service version, we find a score by the 's' letter. For instance the 'crusader' has a 1.0 score meaning a '1' rate for user request and a '0' rate for service request.
All this score are stored in the file bin/state/score.xml updated when you stop the JASon server.
<?xml version="1.0" encoding="ISO-8859-1"?> |
/** |
getServiceContext().cleanWorkingDirectory(
true );
including sub-directories. We write a filegetServiceContext().getWorkingDirectoryFileWriter("message.txt"
)
instruction returning a FileWriter.
Command |
Role |
Result sample |
startService [Service name] |
Start a service. |
|
stopService [Service name] |
Stop a service |
|
listService [*] |
List available services |
scheduler log admin time |
listeService |
List available services with its name, state, version
and information. Here the state number meaning : -1 : No state 0 : Initialized 1 : Stopped 2 : Started |
scheduler 2
v1.1 null log 2 v1.1 null admin 2 v1.1 null time 2 v1.0 null |
listpService |
List properties of the service |
port=20000 |
setService [Service name] [Property] [value] |
Update a service property |
|
putService [JAS file path] |
Add a new service. The service must be in the .JAS
format |
The swing interface uses all previous commands but the setService. For starting the interface, use the client.sh or client.bat commands. Once you have run the interface, you may have to change the connection parameters using the Server/Login menu. Press the Connection button, a tree will be generated with all current JASon services. You can click on a node and start/stop the tied service
(c) 2002 Alexandre Brillant