JMS Application Container Swiftlet
The JMS Application Container Swiftlet provides containers to run JMS applications as a component within the virtual machine of a SwiftMQ router.
These kinds of JMS applications are not different from any other JMS application. They do not have to implement any SwiftMQ interfaces nor do they have to have any special kind of code structure. The only exceptions are an optional method to stop the application by the router and to avoid System.exit()
calls. If a shutdown method is defined, this method is discovered by Java reflection. So there is no proprietary code and therefore no vendor lock-in involved.
A JMS application running within a container typically uses JNDI with a JNDI provider URL
smqp://intravm/timeout=1000
which connects to the router via an intra-VM JMS connection. Once a JNDI InitialContext is created, it obtains an intra-VM connection factory from JNDI:
TopicConnectionFactory tcf = (TopicConnectionFactory)ctx.lookup("IVMTopicConnectionFactory");
All connections created from this connection factory connect to the router also via an intra-VM connection.
How a JMS Application is started
The start of a JMS application takes place by calling its main
method. The arguments to the main
method can be configured. The main
method may block, e.g. due to a loop, or it may return immediately, e.g. if only a message listener has been registered. An attribute main-return-is-stop
can be specified which states whether a return from the main method should mark the application as stopped. The default is false. In that case, the only way to stop is to call the shutdown
method.
How a JMS Application is stopped
A running JMS application is automatically stopped during the router's shutdown sequence. To stop it manually, a shutdown method has to be defined:
Example:
public static void stopMyApp()
{
try
{
connection.close();
} catch (JMSException e)
{
}
}
The method has to be public static
with a return type void
. The method name can be configured and is discovered by Java reflection. Within the shutdown method, all resources should be closed and all threads started from the JMS application, should terminate.
If the application stops by returning from its main
method, the container's attribute main-return-is-stop
must be set to true.
Avoid System.exit
!
A JMS application runs inside the virtual machine of the router, therefore you should avoid to call System.exit()
. For example, instead of this kind of error handling within the main
method:
public static void main(String[] args)
{
if (args.length < 4)
{
System.err.println("<smqp-url> <tcf> <topic> <nmsgs>");
System.exit(-1);
}
...
}
you should use this:
public static void main(String[] args)
{
if (args.length < 4)
{
System.err.println("<smqp-url> <tcf> <topic> <nmsgs>");
return;
}
...
}
How a JMS Application is deployed
The JMS Application Container Swiftlet provides 2 different container types: Static containers and hot deploy containers.
A static container is configurable via SwiftMQ Explorer/CLI and the router's configuration file. It contains definitions of the class name, classpath, system properties, shutdown method, etc. If a shutdown method is defined, the JMS application can be started and stopped by CLI. If not, the application is typically started during the router's startup and stopped during its shutdown.
A hot deploy container listens on a deploy space of the Deploy Swiftlet. Deploying a JMS application takes place by copying a deployment descriptor and at least 1 jar file into the deployed space. The JMS application is automatically started during deployment and stopped during undeployment. A shutdown method is mandatory for hot deployable JMS applications. The deployment descriptor contains the definitions of the JMS application (class name, system properties, etc). Hot deployment enables you to upgrade a JMS application automatically without stopping the router, e.g. deploying a new version of a JMS application with totally different classes into a production system during runtime.
Static Containers
A static JMS application container enables you to run any application (it doesn't have to be a JMS application; you can also start your favorite servlet engine) by configuring the resp. container definitions via the router's configuration file or via SwiftMQ Explorer/CLI. Once it is configured, it can be enabled which means it is started. An enabled JMS application can only be disabled if it provides a custom shutdown method. This method must be public static
with a void
return type. The name of the method is configurable since it is discovered by Java reflection.
Configuration
The configuration of a static JMS application consists of
Name of the class with the
main
method.Optional arguments to the
main
method, delimited by blanks.Attribute
main-return-is-stop
to indicate the application is stopped when returning frommain
.Optional name of the shutdown method.
A startup delay in milliseconds is useful to create a start order of applications.
A list of classpath entries.
A list of system properties to set.
An
enabled
attribute.
The following example shows the configuration of 2 static containers, a subscriber and a publisher, where the publisher is started 20 seconds after the subscriber:
<swiftlet name="sys$jac">
<static-containers>
<static-container name="01"
main-class="TestSubscriber"
main-class-arguments="1 smqp://intravm/timeout=10000 IVMTopicConnectionFactory testtopic 1"
main-return-is-stop="true"
enabled="true">
<classpath>
<path-entry name="01" value="d:/testdriver"/>
</classpath>
<system-properties>
<system-property name="test" value="test"/>
</system-properties>
</static-container>
<static-container name="02"
main-class="TestPublisher"
main-class-arguments="smqp://intravm/timeout=10000 IVMTopicConnectionFactory testtopic 1"
shutdown-method-name="stop"
startup-delay="2000"
enabled="true">
<classpath>
<path-entry name="01" value="d:/testdriver"/>
</classpath>
<system-properties/>
</static-container>
</static-containers>
</swiftlet>
Note that the containers are started in order of the name
attribute of the static-container
element. However, since they run asynchronously in different threads, the startup-delay
ensures that the publisher starts after the subscriber is already running.
The publisher has a shutdown-method-name
defined. The name of the method is stop
. Thus, the class TestPublisher has to have this method:
Example:
public static void stop()
{
// do whatever to stop this app!
try
{
connection.close();
} catch (JMSException e)
{
}
}
The shutdown method enables you to enable/disable (start/stop) the TestPublisher at any time via SwiftMQ Explorer/CLI, so you can easily restart this application. You cannot do this with the TestSubscriber, because it doesn't have a shutdown method. However, since it has the attribute main-return-is-stop
set to true, the container is disabled after TestSubscriber returns from its main
method. Thereafter it can be started again.
Start/Stop a static Application Container
A static JMS application is started by enabling its container via the enabled
attribute. This leads to a call to the main
method of the program in a different thread from the thread pool jac.runner
.
The call to the main method may return immediately or it may block until the program has finished. It is possible to specify if the return from the main
method should mark the container as disabled. This can be done via the attribute main-return-is-stop
. The default value is false.
Therefore, a JMS application within the main
method or with a returned main
method but with attribute main-return-is-stop
set to false is still enabled, thus running. Such an application can only be stopped by invoking the shutdown method.
A JMS application without a shutdown method cannot be stopped, except via the return from the main
method which has to be specified via the attribute main-return-is-stop
.
A shutdown method should always be able to asynchronously close and clean up all resources (e.g. connections, threads, etc) without blocking.
ClassLoaders
Each configured static container uses its own dedicated classloader. The parent class loader is the one that loaded the SwiftletManager class, hence, all SwiftMQ jar files are already present in the classpath and you don't have to define it again.
Since a dedicated classloader is used for each container, it is possible to run the same application in different instances, even if the static main
or a shutdown method will be called. These methods are called on physically different classes, thus all static references within these classes (such as a static connection object) are contained in each container but they are different; a connection.close()
will therefore only close it in one instance but not in the other.
If you delete the configuration of a static container, the classes are physically removed. If you change the application and create a new configuration, the new classes are loaded and you will have the same effect as with hot deployment.
Example
This example shows how to create and use a static container via the SwiftMQ Explorer. We use PubSubPublisher
from the SwiftMQ samples.
In the SwiftMQ Explorer App, click on JMS Application Container Swiftlet
/ Static Containers
:
Click Create a new Entity
and fill the fields. Don't enable it, because you don't have a correct classpath yet.
Select the Classpath
node:
Create a new path entry. The name
specifies the order of the classpath entries. You can create as many entries as you want. The value is the actual path entry. It can be everything you normally specify in classpaths (jars, zips, directories).
If your application needs system properties to be set, click on the System Properties
node:
Specify the system property. You can create as many entries as you want. Keep in mind that these system properties are visible for all applications since they run within the same virtual machine.
Well, that's it. Now you have configured your application.
To start it, toggle the Enabled
attribute. The application will now be started with a delay of 2 seconds. You can see a new entry in the Usage
section:
To stop it, toggle the Enabled
attribute.
Hot Deploy Containers
JMS applications can be hot deployed. In contrast to static JMS application containers, hot deploy JMS application containers are not configurable via CLI or the router's configuration file. Rather, they are configured via their deployment descriptor which is part of the deployment bundle.
Hot deployment enables you to upgrade a JMS application automatically without stopping the router, e.g. deploying a new version of a JMS application with totally different classes into a production system during runtime.
A deployment descriptor can optionally contain a schedule to run the application as a job from the Scheduler Swiftlet, thus, enabling you to run any Java application as a hot deployable job.
The Deployment Bundle
The collection of all necessary files to hot deploy a JMS application is called a deployment bundle. It consists of:
The deployment descriptor,
all necessary jar files,
all necessary native libraries (in case JNI is used).
The Deployment Descriptor
The deployment descriptor is a small XML file with the name config.xml
. It contains similar configuration information as for static JMS application containers:
Name of the class with the
main
method.Optional arguments to the
main
method, delimited by blanks.Mandatory name of the shutdown method.
An optional startup delay in milliseconds, useful to create a start order of applications.
A list of system properties to set.
Optional schedule to schedule the application as a job.
A shutdown method is mandatory for hot deploy JMS applications because it must be stopped during an undeployment. A classpath isn't specified, because the classpath consists of the jar files etc contained in the bundle. Further, a hot deploy JMS application will be automatically started during deployment and stopped before it is undeployed.
The following example deployment descriptor contains the configuration of the TestPublisher example:
<deployment-descriptor>
<main-class>TestPublisher</main-class>
<main-class-arguments>
smqp://intravm/timeout=10000 IVMTopicConnectionFactory testtopic 1000000
</main-class-arguments>
<shutdown-method-name>stop</shutdown-method-name>
<main-return-is-stop>true</main-return-is-stop>
<startup-delay>2000</startup-delay>
<system-properties>
<system-property name="someprop" value="someval"/>
</system-properties>
</deployment-descriptor>
main-class
Contains the fully qualified class name of the class with the main
method.
main-class-arguments
This is optional. Contains the arguments to the main
class, delimited by blanks.
shutdown-method-name
Contains the name of the shutdown method. It has to be public static
with a return type of void
and has to be part of the main
class. The shutdown method must close all resources of the JMS application and must stop all threads started from the application.
Example:
public static void stop()
{
// do whatever to stop this app!
try
{
connection.close();
} catch (JMSException e)
{
}
}
main-return-is-stop
This is optional. Specifies whether a return from the main
method marks the application as stopped.
startup-delay
Contains a millisecond value that is used to delay the start of the application after it has been deployed. This is useful to create a start sequence of hot deploy JMS applications because when the router starts, all hot deployed JMS applications are started asynchronously in different threads.
system-properties
This is optional. Contains system-property
elements to define necessary system properties for the application. These system properties are visible to all JMS applications because they use the same virtual machine.
schedule
A deployment descriptor can contain an optional schedule
element. If specified, the application will be invoked on the schedule from the Scheduler Swiftlet. The management of job registration etc. at the Scheduler Swiftlet takes place automatically. A schedule
element can contain the following attributes:
time-expression: A
at
orrepeat
expression. Mandatory.calendar: Name of the calendar to use. Optional.
date-from: A start date (dd-MM-yyyy). Optional. Default:
now
.date-to: A end date (dd-MM-yyyy). Optional. Default:
fowever
.max-runtime: A maximum runtime. Optional. If not specified, the attribute
main-return-is-stop
must be set to true.logging-enabled: Start/stop of a job instance if logged to SwiftMQ's log file. Optional. Default:
false
.
A schedule is created dynamically at the Scheduler Swiftlet during the deployment and deleted during the undeployment so the elements of the schedule are the same as you have to specify for schedules of the Scheduler Swiftlet. For further infos please take a look there.
The following shows an example of the above deployment descriptor for the TestPublisher
. However, now it is scheduled as a job:
<deployment-descriptor>
<main-class>TestPublisher</main-class>
<main-class-arguments>
smqp://intravm/timeout=10000 IVMTopicConnectionFactory testtopic 1000000
</main-class-arguments>
<shutdown-method-name>stop</shutdown-method-name>
<main-return-is-stop>true</main-return-is-stop>
<schedule time-expression="start 10:00 stop 17:00 delay 10m"
date-to="2003-11-20"
logging-enabled="true"/>
<system-properties>
<system-property name="someprop" value="someval"/>
</system-properties>
</deployment-descriptor>
The Deployment Classes
There has to be at least 1 jar file in the bundle, containing the necessary classes of the application. Of course, it can be more than 1 jar file, however, it is not possible to use plain .classes. You have to jar it. You also cannot use .zip files. If your application needs native libraries, they have to be part of the bundle.
A dedicated class loader is created during deployment, referencing all jar files and native libraries in the bundle. The parent class loader is the one that loaded the SwiftletManager class, hence, all SwiftMQ jar files are already present in the classpath.
Skeleton of a Hot Deploy JMS Application
This is a skeleton of a hot deploy JMS application:
public class JacSkeleton
{
static final Object obj = new Object();
static final boolean shutdownCalled = false;
public static void main(String[] args)
{
// Start your JMS application here
// Then wait here until shutdown is called
try
{
synchronized (obj)
{
if (!shutdownCalled)
obj.wait();
}
} catch (InterruptedException ignored)
{
}
// Now close all resources and terminate
}
public static void shutdown()
{
// Trigger main to stop
synchronized (obj)
{
shutdownCalled = true;
obj.notify();
}
}
}
It ensures that the main
method doesn't terminate until shutdown
is being called.
How To Deploy
The JMS Application Container Swiftlet listens on a deploy space named jms-app
, defined in the Deploy Swiftlet configuration:
<swiftlet name="sys$deploy">
<deploy-spaces>
<deploy-space name="extension-swiftlets" path="../data/deploy"/>
<deploy-space name="jms-app" path="../data/jmsapp"/>
</deploy-spaces>
</swiftlet>
The path defined in the deploy space is the directory where you create your bundles.
Directory Structure
The directory data/jmsapp
is the path of the deploy space jms-app
. There is 1 JMS application deployed. The name is testpub
. This directory is called the "bundle directory". Directly within this directory are stored the deployment descriptor (config.xml
) and 1 jar file (test.jar
). The sub-directory _deployed_...
contains the actual deployment. It is created and managed by the Deploy Swiftlet. It contains copies of the deployment descriptor and the bundle jar files.
Creating a Bundle (Deploy)
Create a bundle directory, e.g. data/jmsapp/testpub
. Copy the deployment descriptor config.xml
and your jar files and/or native libs into this directory. Your application will be deployed within the next check-interval (default 1 minute).
Removing a Bundle (Undeploy)
Remove the deployment descriptor config.xml
and your jar files / native libs out of the bundle directory. Don't touch the actual deployment (_deployed_...
directories)! Your application will be undeployed within the next check-interval (default 1 minute).
Changing a Bundle (Redeploy)
Change either the deployment descriptor config.xml
or change any of the jar files / native libs. Your application will be redeployed within the next check-interval (default 1 minute).
Errors during Deployment
Any error during deployment leads to an undeploy of the bundle. An attempt is made on every check-interval of the deploy space to deploy it again. So in case of an error within your application, you can simply overwrite your jar files with a new version. All errors during deployment are reported to the router's error log file.
Job Scheduling
The following sections describe how to schedule static and hot deploy applications.
How to schedule static Applications
The JMS Application Container registers jobs in job group JAC
at the Scheduler Swiftlet:
These jobs can be scheduled via the Scheduler Swiftlet to run at specific times or in intervals, based on calendars and so on.
Application Invoker
The Application Invoker job invokes a static JMS application whose name must be specified as a job parameter. It requires that the static JMS application has a shutdown method defined, otherwise the job cannot stop it. Either the JMS application must have the attribute main-return-is-stop
set to true or a maximum run time must be specified in the schedule, otherwise, the job runs forever.
The implementation of the Application Invoker job is quite simple. It sets the enabled
attribute to true
on job start and to false
on job stop. Therefore, a static JMS application that should run as a job should be initially disabled.
Parameter | Mandatory | Description |
---|---|---|
Application Name | Yes | Name of the static JMS application. |
How to schedule Hot Deploy Applications
To schedule a hot deploy application is quite easy: Just add a schedule
element to the deployment descriptor:
<deployment-descriptor>
<main-class>TestPublisher</main-class>
<main-class-arguments>
smqp://intravm/timeout=10000 IVMTopicConnectionFactory testtopic 1000000
</main-class-arguments>
<shutdown-method-name>stop</shutdown-method-name>
<main-return-is-stop>true</main-return-is-stop>
<schedule time-expression="start 10:00 stop 17:00 delay 10m"
date-to="2003-11-20"
logging-enabled="true"/>
<system-properties>
<system-property name="someprop" value="someval"/>
</system-properties>
</deployment-descriptor>
A schedule is created dynamically at the Scheduler Swiftlet during the deployment and deleted during the undeployment so the elements of the schedule are the same as you have to specify for schedules of the Scheduler Swiftlet. For further info please take a look there.
Configuration
The configuration of the JMS Application Container Swiftlet is defined within the element
<swiftlet name="sys$jac" .../>
of the router's configuration file.
Element List "static-containers", Parent Element: "swiftlet"
Static Containers. This element list contains zero or more "static-container" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Static Container |
startup-delay | java.lang.Long | No | Delay (ms) to wait before start |
enabled | java.lang.Boolean | No | Enables/Disables this Container |
main-class | java.lang.String | Yes | The class name with the 'main' method |
main-class-arguments | java.lang.String | No | The Arguments of the Main Class (blank is Delimiter) |
main-return-is-stop | java.lang.Boolean | No | Is the return from the 'main' method is equal to stop? |
shutdown-method-name | java.lang.String | No | Optional static Shutdown Method Name |
Values
Attribute | Values |
---|---|
startup-delay | Default: 0 |
enabled | Default: false |
main-class | |
main-class-arguments | |
main-return-is-stop | Default: false |
shutdown-method-name |
Element List "system-properties", Parent Element: "static-container"
System Properties. This element list contains zero or more "system-property" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this System Property |
value | java.lang.String | Yes | Property Value |
Values
Attribute | Values |
---|---|
value |
Element List "classpath", Parent Element: "static-container"
Classpath. This element list contains zero or more "path-entry" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Path Entry |
value | java.lang.String | Yes | Path Entry |
Values
Attribute | Values |
---|---|
value |
Element "usage", Parent Element: "swiftlet"
Running JMS Application Containers.
Element List "statics", Parent Element: "usage"
Running static JMS Application Containers. This element list contains zero or more "static" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Running static JMS Application Container |
start-time | java.lang.String | No | Start Time |
Values
Attribute | Values |
---|---|
start-time |
Element List "hot-deploys", Parent Element: "usage"
Hot Deployed JMS Application Containers. This element list contains zero or more "hot-deploy" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Hot Deployed JMS Application Container |
deploy-time | java.lang.String | No | Deploy Time |
startup-delay | java.lang.Long | No | Delay (ms) to wait before start |
main-class | java.lang.String | No | The class name with the 'main' method |
main-class-arguments | java.lang.String | No | The Arguments of the Main Class (blank is Delimiter) |
shutdown-method-name | java.lang.String | No | Mandatory static Shutdown Method Name |
Values
Attribute | Values |
---|---|
deploy-time | |
startup-delay | Default: 0 |
main-class | |
main-class-arguments | |
shutdown-method-name |
Element List "system-properties", Parent Element: "hot-deploy"
System Properties. This element list contains zero or more "system-property" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this System Property |
value | java.lang.String | Yes | Property Value |
Values
Attribute | Values |
---|---|
value |
Element "Job Schedule", Parent Element: "hot-deploy"
Job Schedule.
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
logging-enabled | java.lang.Boolean | No | If true, start/stop are logged in SwiftMQ's log file |
calendar | java.lang.String | No | Apply this Calendar |
date-from | java.lang.String | No | From Date |
date-to | java.lang.String | No | To Date |
max-runtime | java.lang.String | No | n('s'|'m'|'h'), e.g. 30m |
time-expression | java.lang.String | No | ('at' HH:mm[:ss][, HH:mm[:ss]...]) | ('start' HH:mm[:ss] 'stop' HH:mm[:ss] 'delay' n('s'|'m'|'h' ['repeat' n]) |
Values
Attribute | Values |
---|---|
logging-enabled | Default: false |
calendar | |
date-from | Default: now |
date-to | Default: forever |
max-runtime | |
time-expression |