Instructions for Running James in JBoss

This page describes what you need to do in order to get James to run inside the JBoss application server. Unfortunately this is a pretty ugly process, there are two reasons for this. Firstly the JMXLauncher files have not been tagged with the Phoenix release tags so you need to get the trunk revisions from the CVS repository. Secondly James does not work with the trunk revision of Phoenix because of changes to the lifecycle methods in the underlying Avalon framework and components. The latest revision of Phoenix that James will work with is version 4.0.3.

NOTE: There are two ways to do it: using the source and building it, or just using binaries with the .jar attached to this page. See the binary way below.

Source Way

Get and Build Phoenix

  • Checkout Phoenix version 4.0.3 into a clean directory, the CVS tag for this is Phoenix_4_0_3 and I'll refer to this directory as <phoenix_4_0_3>.
  • Checkout the trunk revision of Phoenix into a different directory, which I'll refer to as <phoenix_head>.
  • Copy the following files from <phoenix_head> to <phoenix_4_0_3>
    avalon-phoenix/src/java/org/apache/avalon/phoenix/components/manager/AbstractJMXManager.java
   avalon-phoenix/src/java/org/apache/avalon/phoenix/components/manager/AbstractSystemManager.java
   avalon-phoenix/src/java/org/apache/avalon/phoenix/components/manager/HostedSystemManager.java
   avalon-phoenix/src/java/org/apache/avalon/phoenix/launcher/FreeNEasyPolicy.java
   avalon-phoenix/src/java/org/apache/avalon/phoenix/launcher/JMXLauncher.java
   avalon-phoenix/src/java/org/apache/avalon/phoenix/launcher/JMXLauncherMBean.java
   avalon-phoenix/src/java/org/apache/avalon/phoenix/launcher/LauncherUtils.java
   avalon-phoenix/src/conf/phoenix-service.xml
  • Build <phoenix_4_0_3>

Install Phoenix in JBoss

  • Copy the phoenix distribution to Jboss directory:
   mkdir %JBOSS_HOME%\phoenix
   copy <phoenix_4_0_3>\dist\* %JBOSS_HOME%\phoenix
  • Copy phoenix loader to Jboss deployment dir (using Jboss 'default' server as example):
   copy <phoenix_4_0_3>\dist\bin\phoenix-loader.jar %JBOSS_HOME%\server\default\deploy
  • Copy phoenix loader config file to Jboss deployment dir (using Jboss 'default' server as example):
<phoenix_4_0_3>\dist\conf\phoenix-service.xml %JBOSS_HOME%\server\default\deploy
  • Edit the phoenix-service.xml file to fix the file paths. Each occurrence of ../../phoenix needs to be replaced with ../phoenix.
  • Edit the phoenix kernel configuration file, %JBOSS_HOME%\phoenix\conf\kernel.xml, to use the hosted JMX system manager:
   <component role="org.apache.avalon.phoenix.interfaces.SystemManager"
              class="org.apache.avalon.phoenix.components.manager.HostedSystemManager"
              logger="manager" >
   </component>
  • Copy the James.sar file to the directory %JBOSS_HOME%\phoenix\apps
  • Run JBoss

Binary Way

I did this with JBoss 3.2.3, James 2.1.3 and Phoenix CVS sources for JMXLauncher.jar as of 3/4/2004. Note that JMXLauncher.jar is put in two places because it contains both JMXLauncher and the HostedSystemManager in one jar--technically, they are conceptually different and could be separated, but one jar made it easier.

  • Download James binary distribution and install it in %JAMES_HOME% (ie. wherever you like). If you use james-2.1.3 and put it in %JBOSS_HOME%, the attached phoenix-service.xml should work as is.
  • Download JMXLauncher.jar and install it in the james phoenix launcher directory:
  • copy JMXLauncher.jar %JAMES_HOME%/bin/lib
  • Copy phoenix-loader.jar, phoenix-service.xml, and JMXLauncher.jar to Jboss deployment dir (using Jboss 'default' server as example):
   copy %JAMES_HOME%\bin\phoenix-loader.jar %JBOSS_HOME%\server\default\deploy
  copy phoenix-service.xml %JBOSS_HOME%\server\default\deploy
  copy JMXLauncher.jar %JBOSS_HOME%/server/default/deploy
  • Edit the phoenix kernel configuration file, %JBOSS_HOME%\phoenix\conf\kernel.xml, to use the hosted JMX system manager:
<component role="org.apache.avalon.phoenix.interfaces.SystemManager" 
              class="org.apache.avalon.phoenix.components.manager.HostedSystemManager" 
              logger="manager">
</component>
  • Run JBoss

Instructions for Running JAMES in Tomcat

  • Download and unzip the Apache JAMES from http://james.apache.org/download.cgi. As of this date version james-2.2.0;http://apache.secsup.org/dist/james/james-2.2.0.tar.gz is available.
  • The PHOENIX_HOME (or JAMES_HOME if you will) should point to the unzipped james folder. It contains the necessary jar files and the config.xml file necessary for the successful invocation of Apache JAMES from Tomcat.
  • The phoenix-loader.jar is designed to startup Apache JAMES from the command line and has to be tailored to work with Tomcat. The entry point is the org.apache.avalon.phoenix.launcher.Main. Here is a working version of Main with functions that were added or modified
public final class Main
{
    private static final String MAIN_CLASS =
        "org.apache.avalon.phoenix.frontends.CLIMain";
    /* 
     * Note: This is changed from phoenix-loader.jar to phoenix-loader-modified.jar
     * for this example 
     */
    private static final String LOADER_JAR = "phoenix-loader-modified.jar";
    private static Object c_frontend;

    /* Comment out the System.exit statement. We work with Tomcat not alone anymore */
    public static final void main( final String[] args )
        throws Exception
    {
        final int exitCode =
            startup( args, new HashMap(), false );
        //System.exit( exitCode );
    }

    public static ClassLoader phoenixHomeClassLoader()
    {
    	File[] libjars = new File(System.getProperty( "phoenix.home" )+"/lib").listFiles(new FilenameFilter() {
    		public boolean accept(File dir, String name) {
    			return (name.endsWith(".jar"));
    		}});
    	File[] binjars = new File(System.getProperty( "phoenix.home" )+"/bin").listFiles(new FilenameFilter() {
    		public boolean accept(File dir, String name) {
    			return (name.endsWith(".jar"));
    		}});
    	File[] binlibjars = new File(System.getProperty( "phoenix.home" )+"/bin/lib").listFiles(new FilenameFilter() {
    		public boolean accept(File dir, String name) {
    			return (name.endsWith(".jar"));
    		}});
    	File[] catalinajars = new File(System.getProperty( "catalina.home" )+"/shared/lib").listFiles(new FilenameFilter() {
    		public boolean accept(File dir, String name) {
    			return (name.endsWith(".jar"));
    		}});
    	List jarURLs = new ArrayList();
    	for (int i=0; i<libjars.length; i++)
    	{
    		try
    		{
    			jarURLs.add(libjars[i].toURL());
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
        }
    	for (int i=0; i<binjars.length; i++)
    	{
    		try
    		{
    			if (!binjars[i].getName().equalsIgnoreCase("phoenix-loader.jar"))
    				jarURLs.add(binjars[i].toURL());
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
        }
    	for (int i=0; i<binlibjars.length; i++)
    	{
    		try
    		{
    			jarURLs.add(binlibjars[i].toURL());
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
        }
    	for (int i=0; i<catalinajars.length; i++)
    	{
    		try
    		{
    			jarURLs.add(catalinajars[i].toURL());
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
        }    	URL[] returnVal = (URL[])jarURLs.toArray( new URL[ jarURLs.size() ] );
    	return new URLClassLoader( returnVal, Main.class.getClassLoader() );    	
    }

    protected static final int startup( final String[] args,
                                        final Map data,
                                        final boolean blocking )
        throws Exception
    {
        int exitCode;
        try
        {
            //setup new Policy manager
            Policy.setPolicy( new FreeNEasyPolicy() );

            //Create engine ClassLoader
            final URL[] urls = getEngineClassPath();
            final URLClassLoader classLoader = new URLClassLoader( urls );

            /* 
             * We build our own classloader which searches for the PHOENIX_HOME directory 
             * for jars 
             */
            ClassLoader commonLibClassLoader = phoenixHomeClassLoader();
            data.put( "common.classloader", commonLibClassLoader );
            data.put( "container.classloader", classLoader );
            data.put( "phoenix.home", new File( findPhoenixHome() ) );            

                        
            //Setup context classloader
            //Thread.currentThread().setContextClassLoader( classLoader );

            //Create main launcher
            final Class clazz = commonLibClassLoader.loadClass( MAIN_CLASS );
            final Class[] paramTypes =
                {args.getClass(), Map.class, boolean.class};
            final Method method = clazz.getMethod( "main", paramTypes );
            c_frontend = clazz.newInstance();

            //kick the tires and light the fires....
            final Integer integer = (Integer)method.invoke(
                c_frontend, new Object[]{args, data, new Boolean( blocking )} );
            exitCode = integer.intValue();
        }
        catch( final Exception e )
        {
            e.printStackTrace();
            exitCode = 1;
        }
        return exitCode;
    }
    /*
     * The other methods namely shutdown, getEngineClassPath, findEngineLibDir, 
     * findPhoenixHome, and findLoaderDir are unmodified and hence not shown here.
     */
  }
  • The file phoenix-loader-modified.jar contains a compiled version of the Main class.
  • Also place the mailet_1_0.jar obtained by unzipping james.sar in the CATALINA_HOME/shared/lib folder.
  • Create a simple servlet to invoke JAMES as follows
public class launchJAMES extends HttpServlet {

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/plain");
        PrintWriter writer = response.getWriter();
        try
        {
            System.setProperty( "phoenix.home", "C:\\Documents and Settings\\Owner\\Desktop\\\\james-2.2.0" );
            System.setProperty( "catalina.home", "C:\\Program Files\\Apache Software Foundation\\Tomcat 5.5");
            URLClassLoader childClassLoader =
       		new URLClassLoader(
		    new URL[] {	          
    		        new File("C:\\Documents and Settings\\Owner\\Desktop\\james-2.2.0\\bin\\phoenix-loader-modified.jar").toURL()
        	});
            final Class mainClass = childClassLoader.loadClass( "org.apache.avalon.phoenix.launcher.Main" );
            final Class[] paramTypes = {args.getClass()};
            final Method method = mainClass.getMethod( "main", paramTypes );
            Object main_instance = mainClass.newInstance();
            method.invoke(main_instance, new Object[]{args} );
        }
        catch (Exception e)
        {
        	e.printStackTrace();
        }
        writer.flush();
        writer.close();
    }
  }
  • Modify the web.xml appropriately to add the servlet name. Start Tomcat and deploy the webapp.
  • Open a browser and point to the servlet to invoke JAMES from within Tomcat
  • To shutdown use the JAMES Remote Administration Tool to shutdown JAMES as well as Tomcat.

Hints

  • The ClassNotFoundException is the most common exception. This is a ClassLoader issue and using the JAVA_VM_ARGS=-verbose flag when starting Tomcat drops a few hints at possible problem areas.
  • The mail-*.*.jar used by Tomcat and that by JAMES have to be of the same version.
  • No labels