IntraVM

Introduction

The intra-VM JMS client is a client which is connected with a SwiftMQ router via an intra-VM connection. This is not a socket connection but a virtual connection from the JMS client part to an intra-VM connector in the Network Swiftlet. Both the SwiftMQ router and one or more JMS clients are running in the same virtual machine. This requires to start the router as the first action to be able to start intra-VM clients thereafter.

Using the intraVM is the usual way if a SwiftMQ router is an embedded messaging system in an OEM product, for example. Another way is to use the JMS Application Container Swiftlet which has JMS containers to launch JMS applications either during the router start and/or during a hot deployment, respectively. The JMS Application Container Swiftlet extends SwiftMQ to a JMS application server so you might have a look there.

The actual application code of an intra-VM JMS client doesn't differ from a remote JMS client, except the JNDI provider URL and the name of the connection factory to use. So there is no proprietary code and therefore no vendor lock-in involved here:

      include java.util.*;
      include javax.naming.*;
      include javax.jms.*;
      ...
      // Create the Hashtable with the JNDI environment properties
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.swiftmq.jndi.InitialContextFactoryImpl");
      // Use an intraVM JMS connection for the JNDI connection
      env.put(Context.PROVIDER_URL, "smqp://intravm/timeout=10000");
      InitialContext ctx = new InitialContext(env);
      // Lookup a connection factory and a topic
      TopicConnectionFactory tcf = (TopicConnectionFactory) ctx.lookup("IVMTopicConnectionFactory");
      Topic topic = (Topic) ctx.lookup("testtopic");
      ctx.close();
      // Create a topic connection
      TopicConnection connection = tcf.createTopicConnection();
      ...

The JNDI provider URL here is "smqp://intravm/timeout=10000" where "intravm" is a keyword and instructs SwiftMQ's JNDI context to create an intra-VM connection. The "IVMTopicConnectionFactory" is defined as an intra-VM connection factory. Once the application creates a connection from it, it creates an intra-VM connection.

Launching SwiftMQ

Before an intra-VM JMS client can be used, the SwiftMQ router must be started. For example, a private method with the startup statements can be used and can be called during the initialization.

Example:

      import com.swiftmq.swiftlet.*;
      ...
      private static void startSwiftMQ(String workingDir, String configFile, boolean hook)
        throws Exception
      {
        if (!hook)
          System.setProperty("swiftmq.shutdown.hook","false");
        SwiftletManager.getInstance().setWorkingDirectory(workingDir);
        SwiftletManager.getInstance().startRouter(configFile);
      }

      private static void shutdownSwiftMQ()
      {
        SwiftletManager.getInstance().shutdown();
      }

      public static void main(String[] args)
      {
        // Start SwiftMQ
        try {
          startSwiftMQ("d:/swiftmq_4_0_0/scripts/win32",
                       "../../config/router1/routerconfig.xml",
                       true);
        } catch (Exception e)
        {
          e.printStackTrace();
          System.exit(-1);
        }
        // Start my application
        ...
        // Shutdown SwiftMQ
        shutdownSwiftMQ();
      }

Shutdown Hook

The "startSwiftMQ" method contains a condition to set a system property "swiftmq.shutdown.hook". This property is used to determine whether the SwiftMQ router registers a shutdown hook during startup. A shutdown hook is used to perform an orderly shutdown if the router's process is terminated with ctrl/c or a kill command. A shutdown hook must not be registered if the application registers an own shutdown hook and calls "SwiftletManager.getInstance().shutdown();" within that hook like most application servers do.

Working Directory

The statement "SwiftletManager.getInstance().setWorkingDirectory(workingDir);" sets the working directory of the router which is the directory where the startup scripts like "smqr1" are located. This is "<routerdir>/scripts/win32" or "<routerdir>/scripts/unix", respectively. The reason why this must be the working directory is simply because all references in the "routerconfig.xml" are relative to this. So if you specify another working directory you would have to change all references.

Starting the Router

The last statement "SwiftletManager.getInstance().startRouter(configFile);" starts the router and returns when all Kernel Swiftlets have been successfully started. The "configFile" parameter specifies the name of the router's configuration file. This is relative to the working directory.

Stopping the Router

The private method "shutdownSwiftMQ" contains a single statement "SwiftletManager.getInstance().shutdown();" to explicitly shutdown SwiftMQ. It returns after all Swiftlets have been stopped.

Classpath Settings

To launch SwiftMQ intra-VM you need all jar files from the "<routerdir>/jars" directory in your classpath.

Connection Factories

Connection factories for intra-VM connections are located below the element "intravm-connection-factories" in the JMS Swiftlet configuration. They are similar to normal connection factories, except they have no network buffer attributes.

The following configuration of the JMS Swiftlet shows the definition of 2 intra-VM connection factories:

  <swiftlet name="sys$jms">
    <intravm-connection-factories>
      <intravm-connection-factory name="IVMQueueConnectionFactory"/>
      <intravm-connection-factory name="IVMTopicConnectionFactory"/>
    </intravm-connection-factories>
    <listeners>
      <listener name="plainsocket" port="4001">
        <connection-factories>
          <connection-factory name="QueueConnectionFactory"/>
          <connection-factory name="TopicConnectionFactory"/>
          <connection-factory name="plainsocket@router1"/>
          <connection-factory name="plainsocket_appserver@router1"
                                 thread-context-classloader-for-getobject="true"/>
          <connection-factory name="plainsocket_recover@router1" smqp-consumer-cache-size="10"/>
        </connection-factories>
        <host-access-list/>
      </listener>
    </listeners>
  </swiftlet>

An intra-VM connection factory contains various attribute definitions which define the behavior of the JMS clients, connecting via the connection factory:

Attribute Default Meaning
jms-client-id null Presets the JMS client id. If none is specified (default), a random client id is created. Note that it is not possible to create a durable subscriber without a client id or with a random one. The router will reject it.
jms-default-delivery-mode persistent Presets the JMS default delivery mode for message producers. The default value is compliant with the JMS specification.
jms-default-message-ttl 0 Presets the JMS default message time-to-live for message producers. The default value is compliant with the JMS specification.
jms-default-message-priority 4 Presets the JMS default message priority for message producers. The default value is compliant with the JMS specification.
jms-default-message-id-enabled true Presets whether a message id should be generated for produced messages. The default value is compliant with the JMS specification.
jms-default-message-timestamp-enabled true Presets whether a time stamp should be generated for produced messages. The default value is compliant with the JMS specification.
thread-context-classloader-for-getobject false Presets whether the thread contex class loader should be used during the deserialization of objects returned from an ObjectMessage' getObject() method. This is important if SwiftMQ is integrated into app servers which use different class loaders for message-driven beans.
smqp-producer-reply-interval 20 Contains the interval after which a message producer waits on a reply and thus can act on flow control delays. This attributes takes only effect for non-persistent messages. The default value is optimal in conjunction with a smqp-consumer-cache-size of 500.
smqp-consumer-cache-size 500 Contains the size of the client message cache of message consumers. The router sends up to this size without waiting for a reply. This attribute effects performance and the default value is optimal in conjunction with a smqp-producer-reply-interval of 20.
smqp-consumer-cache-size-kb 2048 Limits the size of the cache per consumer to the specified value in KB. A value of -1 disables it. Limiting the size is useful to avoid out of memory errors if large messages are transfered. Since 7.5.0.

Client-Side Threadpools

An intra-VM JMS client utilizes 2 thread pools, one to drive sessions, and one to drive connection outbound writes (client to router). In contrast to a remote JMS client, threads of an intra-VM JMS client are scheduled in thread pools from the Threadpool Swiftlet. The advantage is the better monitoring and management via SwiftMQ Explorer, for example.

The following thread pools are used from intra-VM JMS clients:

  • jms.ivm.client.connection
  • jms.ivm.client.session

The default configuration of this pools are:

    <pool name="jms.ivm.client.connection" kernel-pool="true" max-threads="10" min-threads="1">
      <threads>
        <thread name="sys$jms.client.connection.%"/>
      </threads>
    </pool>
    <pool name="jms.ivm.client.session" kernel-pool="true" max-threads="10" min-threads="1">
      <threads>
        <thread name="sys$jms.client.session.%"/>
      </threads>
    </pool>

Details of that configuration are described in the Threadpool Swiftlet documentation.