Jiplet Advanced Developer Guide

This is a continuation of the Jiplet Developer Guide. Before you read this document, please make sure that you have read the document and and its prerequisites. This document also uses the same conventions used in the Jiplet Developer Guide.

Table of Content

Using naming context

The jiplet container running as a Jboss service provides a JNDI InitialContext implementation instance to jiplet applications running under it, in a manner that is similar to those provided by a Java2 Enterprise Edition application server. Entries in this InitialContext may be referenced by the following elements in the jip application deployment descriptor (/JIP-INF/jip.xml) of your jiplet application:

  • <env-entry> – Environment entry, a single-value parameter that can be used to configure how the application will operate.
  • <resource-ref> – Resource reference, which is typically to an object factory for resources such as a JDBC DataSource, a JavaMail Session, or custom object factories configured into Tomcat 4.
  • <resource-env-ref> – Resource environment reference, a new variation of resource-ref added in Servlet 2.3 that is simpler to configure for resources that do not require authentication information.
  • <ejb-ref> – EJB reference for referring to EJBs that you jiplet application needs to access.
  • <ejb-local-ref> – Ejb local reference for referring to the local interface of the EJB.

For more details on the above entries, please refer to the comments in the jip.xml deployment descriptor. Optionally, you can add another server-specific deployment descriptor where you can map the resource names for the above entries to the JNDI names. This file must be names jboss-jip.xml and must be placed under the /JIP-INF directory of the context. If you do not have the jboss-jip.xml descriptor file or do not have matching entries between the two files, the jiplet container will assume that the name is the same as the JNDI name. The following example shows an example of the entries in the jip.xml and jboss-jip.xml descriptor files:

jip.xml (part) jboss-jip.xml (part)
<resource-ref>
<res-ref-name>mail/SipExchange/Mail
</res-ref-name>
<res-type>javax.mail.Session
</res-type>
<res-auth>Container
</res-auth>
</resource-ref>
<ejb-ref>
<ejb-ref-name>ejb/SipExchange/RoleRegistrar
</ejb-ref-name>
<ejb-ref-type>Session
</ejb-ref-type>
<home>org.cafesip.sipexchange.ejbs.subscriber.RoleRegistrarHome
</home>
<remote>org.cafesip.sipexchange.ejbs.subscriber.RoleRegistrar
</remote>
<ejb-link>RoleRegistrar
</ejb-link>
</ejb-ref>
<resource-ref>
<res-ref-name>mail/SipExchange/Mail
</res-ref-name>
<jndi-name>java:/SipExchangeMail
</jndi-name>
</resource-ref>
<ejb-ref>
<ejb-ref-name>ejb/SipExchange/RoleRegistrar
</ejb-ref-name>
<jndi-name>ejb/SipExchange/RoleRegistrar
</jndi-name>
</ejb-ref>

Once you have defined the entries in the jiplet deployment descriptors, you can access them from your jiplet application using the standard Java Naming API. For example:

Context ctx = new InitialContext(System.getProperties());
Session session = (Session)ctx.lookup(“java:comp/env/mail/SipExchange/Mail”);
LocationRegistrarHome loc_home = (LocationRegistrarHome)ctx.lookup(“java:comp/env/ejb/SipExchange/RoleRegistrar”);

Note the use of “java:comp/env” sub-context.

The SipExchange project uses this feature. For more details on usage, download the source distribution and look at the org.cafesip.sipexchange.jiplets package.

Notes:

  1. We are still working on this feature and therefore some of the features that you may find in a standard J2EE applications may be missing including authentication.
  2. This feature does not currently work with the standalone jiplet container. In the standalone jiplet container, the above entries are ignored by the jiplet container.

Handling external events

A jiplet application may need to interact with external entities that are either running outside the jiplet container’s JVM or running as a separate context. For example, a jiplet may need to communicate with another application using TCP sockets. Sending TCP messages is quite straight forward as Java already provides classes for that. The question is  – how does a jiplet get notified when the external application sends a TCP message?

The jiplet container supports “signals” from external entities. A signal is a way for external entities to notify a jiplet of certain events. When an external entity sends a signal to a jiplet, the jiplet’s processSignal() method is invoked. The jiplet applications needs to put in the business logic of handling the signal in the body of the method processSignal(). A signal is a generic mechanism and can handle any type of external events – a message from a socket, a JMS message, native events, etc. However, your jiplet application must provide the core functionality of handling external events.

The reference jiplet application that you can download separately includes an example of signal handling. The signal handling code can be found in the class org.cafesip.reference.jiplet.SipRegistrar class. When the SipRegistrar jiplet is being initialized by the jiplet container, the init() method is called. Inside the init() method, this jiplet starts a new thread that listens for TCP connections. When a connection is received, it opens a socket and listens for messages. A message is a single line text message. When a message is received, the message is “signaled” to the SipRegistrar jiplet. The jiplet prints a log message.

SipRegistrar.init() method:

……
try
        {
            // start an external entity so that it can send events to the jiplet when a TCP message is
            // received from an external application. This illustrates how to use signaling.
            entity = new ExternalEntity(getJipletContext(), getName(), -1);
            entity.start(); // start the thread
        }
        catch (IOException e)
        {
            error(“Could not start the external entity : ” + e.getMessage()
                    + “\n” + JipletLogger.getStackTrace(e));
        }

The ExternalEntity class, above, handles the TCP connection and receves message from sockets.

ExternalEntity.run() method:

   public void run()
    {
        try
        {
            while (isInterrupted() == false)
            {
                Socket s = socket.accept();
                JipletLogger.info(“New socket connection received from ” + s.getRemoteSocketAddress());
                BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String line = “”;
                while (line != null)
                {
                    line = reader.readLine();
                    if (line == null)
                    {
                        JipletLogger.info(“Connection closed from the remote socket”);
                        break; // accept another connection
                    }
                   
                    line = line.trim();
                    if (line.length() == 0)
                    {
                        // ignore blank line
                        continue;
                    }
                   
                    if (line.equals(“quit”) == true)
                    {
                        JipletLogger.info(“A quit command is received. Going to close the connection”);
                        reader.close();
                        s.close();
                        break; // accept another connection
                    }
                   
                    // pass on the received message as a signal to the jiplet
                    JipletSignal signal = new JipletSignal();
                    signal.setEventObject(line);
                   
                    if (context.sendSignal(name, signal) == false)
                    {
                        JipletLogger.error(“Error sending signal to the jiplet”);
                    }                   
                }
            }
        }
        catch (Exception e)
        {
            JipletLogger.fatal(“Exception ” + e.getClass().getName() + ” : “
                    + e.getMessage() + “\n” + JipletLogger.getStackTrace(e));
        }
    }

When a message is received, and the message is not “quit”, the message is passed on to the jiplet using org.cafesip.jiplet.JipletContext.sendSignal() method.  To pass the message itself, the org.cafesip.jiplet.JipletSignal class is used as shown in the above code segment.

SipRegistrar.processSignal() method:

    public void processSignal(JipletSignal signal)
    {
        super.processSignal(signal); // print the debug message
       
        String message = (String)signal.getEventObject();
        JipletLogger.info(“Received a signal with message ” + message);
    }

The processSignal() method shown above work in a manner similar to the method processRequest(), processResponse(), etc. You can set scoped variables, and forward the signal to another jiplet.

For more details, see the javadocs for the classes org.cafesip.jiplet.JipletSignal, org.cafesip.jiplet.JipletContext and org.cafesip.jiplet.Jiplet.

To try out the above example, install the reference application as explained here. Open a command prompt (Windows) or terminal (Linux/Unix), run the following commands:

> telnet localhost 9998
Type in some text

You should see the text printed by the jiplet. If you type quit, the connection will be closed.

Classloader architecture

The jiplet container uses different classloaders than the one provided by the Java virtual machine (the system classloader). There are two reasons for it.

  1. Using different class loaders for every jiplet context makes the applications isolated. It makes it impossible for applications to access each others classes.
  2. The custom class loader also allows us to load classes and jar files not specified in the CLASSPATH.

Boot classloader

The boot classloader is the classloader that is used to start up the jiplet container.
Standalone

When running in standalone mode, the startup script creates a CLASSPATH that loads all the classes in the directory $JIPLET_HOME/boot and all the jar files in the $JIPLET_HOME/lib directory. The main class is org.cafesip.jiplet.standalone.boot.Main. This class creates a custom classloader that loads all the classes from the $JIPLET_HOME/class, $JIPLET_HOME/common/classes and jar files from $JIPLET_HOME/common/lib directories. The $JIPLET_HOME/common directory is used to load user-defined classes and jar files.

Jboss

When running as a Jboss service, the jiplet container starts up  uses the classloader the Jboss server provides for the jiplet container service. The CLASSPATH includes all the jar files and classes packaged inside the jiplet.sar. If you want to load some classes that you want to be available to all the contexts, you can copy the jar files into Jboss server lib directory as explained below.

Context classloaders

For every jiplet context that the jiplet container creates, it creates a new classloader and loads the context classes using this classloader. This classloader’s parent classloader is the boot classloader. That is, this classloader inherits the CLASSPATH of the boot classloader. The context classloaders load the classes and jar files from the “classes” and “lib” directories respectively under the deployment directory (or archive = SPR) of the context.

Therefore, the contexts are isolated from each other as far as the classloading is concerned.

Realm classloaders

For every deployable realm (explained below), a new classloader is created and the realm classes are instantiated using this classloader. This classloader’s parent classloader is the boot classloader. That is, this classloader inherits the CLASSPATH of the boot classloader. The context classloaders load the classes and jar files from the “classes” and “lib” directories respectively under the deployment directory (or archive = SRR) of the realm archive.

Loading classes common to all contexts/realms

Sometimes, you will need to add classes or a package that are accessible from multiple contexts or realms. For instance, you want to add the Oracle JDBC driver classes to be accessible from multiple jiplet applications. In order for you applications to use these classes, you will need to include them into the CLASSPATH of the boot classloader.

For the Jboss environment. copy the classes packaged into a jars in the $JBOSS_HOME/server/$RUN/lib/ directory.

For the standalone environment, copy the classes into $JIPLET_HOME/common/classes directory and the jar files into $JIPLET_HOME/common/lib directory.

There are many uses of this. For example:

  1. You are using the JDBC realm provided by the jiplet container for CMAA and want to use an Oracle database instead of the MySQL database, you can copy the Oracle JDBC driver jar file into the directory and make appropriate modification to the configuration file – server.xml. The class loader will automatically load the classes for the Oracle driver.
  2. If you are creating your own realm, you can load the realm class and any other supporting classes in this directory for the system to load it during initialization.
  3. If you want to replace the NIST JAIN-SIP stack provided by us, you can get a new stack classes from another vendor, copy them into this directory (delete the old JAIN-SIP jar files) and make appropriate modifications to the server.xml file.

Creating your own realm implementation

The jiplet container supports container-managed authentication and authorization (CMAA). Realms or authentication databases can be configured using which the jiplet container authenticates the users and verifies their roles (or privileges). When a SIP request message is received by the container, it can perform authentication as specified in RFC 2617 before handing the message over to a jiplet. By default, the jiplet container provides a memory realm and a JDBC realm. This is explained in details in the features FAQ, installation and configuration howtos. You may be able to use the realms provided by us as your user database. But you may already have a subscriber database and want to use it instead. It is possible to add your own realm to the jiplet container. You will need to develop some Java code for this.  Before doing that, you will need to understand a little more about realms.

There are two types of realms. They are:

  1. System realms: System realms are either bundled with the jiplet container package or they are defined in the server.xml file. These realms are typically shared by multiple jiplet contexts.
  2. Deployable realms: Sometimes a jiplet context requires to install a realm for authentication and authorization for the jiplet context only. These realms can be deployed and undeployed in a manner similar to the jiplet contexts. The deployable realms are packaged into an SRR archive (similar to SPR archive for a jiplet context) or directory and deployed. These realm can also be deployed and undeployed from the jiplet console.

System realm development and deployment

To develop and deploy system realms, the steps are:

  1. Create a Java class (MyRealm, for example) that implements org.cafesip.jiplet.Realm interface. Read the javadocs for this class for details on the abstract methods that you have to implement.
  2. Alternatively, create a Java class (MyRealm, for example) that extends org.cafesip.jiplet.realms.BaseRealm class. Read the javadocs for this class for details on the abstract methods that you have to implement as well as other assumptions. This way of creating a realm is easier than the former method because the BaseRealm class is doing most of the work for you.
  3. Compile the java classes.
  4. This step is optional. Package the class and other helper class that you have created into a jar file.
  5. Copy the packaged jar file or the appropriate classes into the CLASSPATH of the boot classloader (Click here for more details).
  6. Modify the server.xml file to configure the realm. See the documentation in the server.xml file for more details.
  7. Re-start the jiplet container for the change to take effect.
  8. Create/Modify the security-constraint element for the jiplet application to use this realm. The modification is required in the jip.xml file for your application. See the comments on this file for detailed information on how to setup security constraints.
  9. Deploy the jiplet application and test it out.
  10. To undeploy, remove the realm definition from the server.xml file.

Deployable realm development and deployment

Developing a deployable realm

It involves the following steps:

  1. Create a Java class (MyRealm, for example) that implements org.cafesip.jiplet.Realm interface. Read the javadocs for this class for details on the abstract methods that you have to implement.
  2. Alternatively, create a Java class (MyRealm, for example) that extends org.cafesip.jiplet.realms.BaseRealm class. Read the javadocs for this class for details on the abstract methods that you have to implement as well as other assumptions. This way of creating a realm is easier than the former method because the BaseRealm class is doing most of the work for you.
  3. Copy the java classes.
Packaging the realm

Please follow the steps below:

  1. Create a deployment directory (lets call it my-realm).
  2. Create directories META-INF, classes and lib under the deployment directory.
  3. Copy the realm classes (.class file) and any other helper classes under the classes directory. Please make sure that the directory hierarchy required for the java package is maintained. For example, if the class is in the package org.cafesip.realms.MyRealm, you must create sub-directories org/cafesip/jiplet/realms under the classes directory and place the MyRealm.class under that directory.
  4. If the class require any jar files, copy them under the lib directory.
  5. Create a deployment descriptor file under the META-INF directory. The file must be called realm.xml and looks something like the following:
    <realm name=”subscribers.sipexchange.cafesip.org”
        classname=”org.cafesip.sipexchange.realms.SipExchangeRealm”
        default=”false”>
        <realm-params>
            <realm-param>
               <realm-param-name>nonce-private-key</realm-param-name>
               <realm-param-value>spice</realm-param-value>                   
            </realm-param>
           
            <realm-param>
               <realm-param-name>domain-uri</realm-param-name>
               <realm-param-value>sip:cafesip.org</realm-param-value>                   
            </realm-param>
           
            <realm-param>
               <realm-param-name>subscriber-registrar-ejb-ref</realm-param-name>
               <realm-param-value>ejb/SipExchange/SubscriberRegistrar</realm-param-value>                   
            </realm-param>
     
            <realm-param>
               <realm-param-name>dir-url</realm-param-name>
               <realm-param-value>jnp://localhost:1099</realm-param-value>                   
            </realm-param>               
        </realm-params>
    </realm>Basically, the content of realm.xml is the same as the <realm> definition found in the server.xml file. Please see the comments on this file  for details.
  6. Optionally, you can create an archive file. The archive file zips the above deployment directory into a single archive file. The file must have an extension “.srr” and is referred to as a SRR file. An example of an archive file name is sipex.srr.  We have provide a custom ant task to create a SRR file. The ant task extends the “jar” ant task and has the same attributes. The following ant code segment demonstrates the usage of the ant task:        
           <taskdef name=”srr” classname=”org.cafesip.jiplet.ant.SrrTask”>
                <classpath>
                    <fileset dir=”${project.lib.root}”
                     includes=”jiplet.jar”/>
                </classpath>
            </taskdef>
          
            <srr destfile=”${project.home}/sipex-realm.srr”
                basedir=”${project.deploy.root}/sipex-realms”
                includes=”**/*”/>Note that it is not necessary to create a SR archive but it does have certain advantages especially when deploying the realm using the jiplet console.
Deploying the realm

Deploying a realm is very similar to deploying a jiplet context. You just have to copy the realm deployment directory or the archive you created above into the jiplet container’s deployment directory.

Standalone jiplet container:

  1. Copy the realm directory or the SRR file into the $JIPLET_HOME/deploy directory. Re-start the jiplet container and the jiplet container will start this realm on initialization.
  2. Alternatively, you can use the jiplet console to add the new realm. In this case, you do not have to re-start the container. You can also upload the SRR file from a client machine using the upload feature.

Jiplet Container running as a Jboss service:

  1. Copy the realm directory or the SRR file into the Jboss deployment directory. Jboss will automatically deploy the realm.
  2. Alternatively, you can use the jiplet console to add the new realm.  With this approach, you can also upload the SRR file from a client machine using the upload feature.

Packaging a jiplet context into an enterprise archive (EAR)

If you are running the jiplet container as a Jboss service, you can bundle all the J2EE components including war, ejb-jar, spr, and srr archives that your application needs into a enterprise archive (EAR in the J2EE terminology). You just have to follow the standard procedure required for creating an ear.  Namely:

  1. Create a directory where you will be staging the ear deployment.
  2. Have all the individual archives (like war,ejb-jar spr and srr) built and copied to the staging area.
  3. Create a deployment descriptor called application.xml and place it under the META-INF directory under the staging area.
  4. Create the archive from the staging area using the ANT task “ear” or using the “jar” utility. The file name must have an .ear extension.

The following shows an example of the application.xml deployment descriptor that includes srr and spr archives:

<application xmlns=”http://java.sun.com/xml/ns/j2ee”
      xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
      xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee
      http://java.sun.com/xml/ns/j2ee/application_1_4.xsd”
      version=”1.4″>

  <display-name>SipExchange</display-name>
   
  <module>
    <ejb>sipex-location.jar</ejb>
  </module>
 
  <module>
    <ejb>sipex-subscriber.jar</ejb>
  </module>

  <module>
    <java>sipex-realm.srr</java>
  </module>
 
  <module>
    <java>sipex-jiplets.spr</java>
  </module>
 
</application>

Ordering of modules

One of the interesting issues is the order in which you want the modules inside EAR to start. For example, in the above application.xml deployment descriptor, you may want the EJB jars to start first followed by the realm (SRR) and then the SIP archive (SPR) because one module may be depending on another module.

By default, Jboss determines the ordering based on the order value provided by the system deployers (war, ejb3, wsr, etc.). The spr and srr deployers provided by the jiplet container specifies the order to Jboss similar to how the system deployers specify their orders. Based on the ordering, Jboss, starts a service archive (sar) first, followed by EJB jars, followed by the  SIP realm (srr) archives, followed by the SIP jiplet (spr) archives,  followed by the web archives (war), followed by the web services (wsr) archives. If you like this ordering, you don’t need to do anything. But you can override them if your application needs applications to start differently. There are tow ways of doing it:

Modify the default DeploymentSorter provided by Jboss:
You will have to change the URL Comparator of Jboss specified in the jboss-service.xml file. In order to do this follow the steps outline below:

  1. With a text editor of your choice, open the file $JBOSS_HOME/server/$RUN/conf/jboss-service.xml
  2. Search for a XML element that looks like:<mbean code=”org.jboss.deployment.scanner.URLDeploymentScanner”
                  name=”jboss.deployment:type=DeploymentScanner,flavor=URL”>
  3. Read the detailed comments Jboss has provided to understand the Jboss’s deployment scanner logic.
  4. Replace the element <attribute name=”URLComparator”>org.jboss.deployment.DeploymentSorter</attribute>With <attribute name=”URLComparator”>org.jboss.deployment.scanner.PrefixDeploymentSorter</attribute>
  5. Now, use a prefix in front of the module names to dictate the startup order. A modified version of the application.xml may look like the following:<module>
        <ejb>sipex-params.jar</ejb>
      </module>
       
      <module>
        <ejb>sipex-location.jar</ejb>
      </module>
     
      <module>
        <ejb>sipex-cdr.jar</ejb>
      </module> 
     
      <module>
        <ejb>sipex-subscriber.jar</ejb>
      </module>
            
      <module>
        <java>01sipex-realm.srr</java>
      </module> 
     
      <module>
        <web>
          <web-uri>sipconsole.war</web-uri>
          <context-root>sipconsole</context-root>
        </web>
      </module>   <module>
        <java>02sipex-jiplets.spr</java>
      </module>

     

    Note the use of 01sipex-realm.srr and 02sipex-jiplets.spr. Make sure that the srr and spr files have the same names.

  6. Restart Jboss. On deployment of the EAR, the SRR module will be deployed before the SPR module.

Override the default ordering
You can override the order in which Jboss deploys archives by modifying the file – org.jboss.deployment.MainDeployer-xmbean.xml. In order to do this follow the steps outline below:

  1. With a text editor of your choice, open the file $JBOSS_HOME/server/$RUN/conf/xmdesc/org.jboss.deployment.MainDeployer-xmbean.xml
  2. Search for a XML element that looks like:     <descriptors>
             <value value=
               ”250:.rar,300:-ds.xml,400:.jar,500:.war,550:.jse,650:.ear,800:.bsh”/>
          </descriptors>
  3. Read the detailed comments Jboss in order to understand the how to modify the order.
  4. Make the necessary changes and save the changes.
  5. Re-start Jboss for the change to take effect.

 

 

 

VN:R_U [1.9.20_1166]
Rating: 0.0/10 (0 votes cast)

Leave a Reply