Print
Making compatible components

Introduction

Quite often reusable components are made elsewhere. Codehaus, Apache, Sourceforge etc. have a number of places where this activity is going on. While we get it right most of the time, some components developed elsewhere are harder to use in Loom.

Things to remember

There are a number of common sense things to remember when making or adapting a Java component to be reusable in Loom as block.

Beanification

  • Have a public empty constructor for your main class
  • Have setters for its configuration.
  • Do not assume that the File is where dependancies are - people may reuse this in jars, applets etc.
  • Divorce your main method (if appl) from your main class - Loom does not call main methods.
  • Consider that the setup and initialization of the bean does not happen in the constructor - as a convenience to the user, have an initialize() method
  • If the comp has start/stop functinality consider having start() and stop() methods.
  • Try to avoid Singleton concepts. There could be multiple blocks in one sar using differnt (by design) instances of your bean

Inversion of Control Pattern

The IoC pattern is described here. This means for Loom avoiding static concepts including loggers.

Separation of interface and implementation

The separation of interface/implementation pattern is described here. For Loom this means we can (if done completely) mount the implementation jar in place where hosted client components (beans, servlets etc) can use the API, but not see the implementation. We can also reimplement or wrap bits of the implementation. For example we could write a pluggable implementation that could, for a certain API journal some methods, but still delegate to the real implementation. Which pluggable implementation is used by Loom when it boots is determined in assembly.xml of course.

Opening up the API

Given that you have divided into interface and implementation, there are probably plenty of methods you can put method in the interface you never though might be used. For example if you are making JDBC compliant relational database, and it is a bean, you could easily think that the only use would be clients via JDBC over sockets. Well, given that Loom can now mount the RDBMS block, it might want to be reused by other blocks that other people have developed inside the the same SAR file. In that case have beanlike methods of ...

  1. Database createDatabase( String name )
  2. Database cloneDatabase( String name )

.. might be useful. Just because you can only see a ServerSocket interface does not mean that others do.

Example compatible component

Below are an interface and implementation that are suitably separated, are beanlike and is in accordance with the IoC pattern...

package examplecomp;
public interface WebServer
{
    void mountWar( String contextName, URL pathToWar );
    void unMountWar( String contextName );
}

package examplecomp.server;
public class MyWebServer implements WebServer
{
    public MyWebServer()
    {
        // whatever.
    }
    public void setPort( int port )
    {
        // this is one configuration item.
    }
    public void initialize()
    {
        // whatever.
    }
    public void start()
    {
        // whatever.
    }
    public void stop()
    {
        // whatever.
    }
    public void mountWar( String contextName, URL pathToWar )
    {
        // whatever.
    }
    public void unMountWar( String contextName )
    {
        // whatever.
    }
}

For standalone mode, it might be launched like so:

package examplecomp.main;
public class WebServerMain
{
    public static void main( String[] args ) throws Exception
    {
        MyWebServer webServer= new WebServer();
        webServer.setPort( Integer.parseInt( args[0] ) );
        webServer.initialize();
        webServer.start();
        webServer.mountWar( args[1], new File( args[2] ).toURL() );
    }
}

When we are trying to run this in Loom we might have this wrapper:

package examplecomp.block;
public class WebServerBlock
    extends AbstractLoggable
    implements WebServer, Startable, Configurable, Initializable
{
    private int m_port;
    private WebServer m_webServer;

    public WebServerBlock()
    {
        m_webServer = new MyWebServer();
    }

    public void configure( final Configuration configuration )
        throws ConfigurationException
    {
        m_port = configuration.getChild( "port" ).getValueAsInteger( 8080 );
    }

    public void initialize() throws Exception
    {
        m_webServer.setPort( m_port );
        m_webServer.initialize();
    }

    public final void start() throws Exception
    {
        m_webServer.start();
    }

    public void stop() throws Exception
    {
        m_webServer.stop();
    }

    public void mountWar( String contextName, String pathToWar )
    {
        m_webServer.mountWar( contextName, pathToWar );
    }

    public void unMountWar( String contextName )
    {
        m_webServer.unMountWar( contextName );
    }
}

This basically shows the implementation wrapped and taking its configuration from the config.xml that Loom prefers from configuration. If the developer wanted they could ignore that place of configuration and use their own config files. If the WebServer block were being reused by another Loom block (say an EJB server), it might be like so:

package somebeanserver;
public class EJBBlock
    extends AbstractLoggable
    implements Composable
{
    private WebServer m_webServer;

    public void compose( final ComponentManager componentManager )
        throws ComponentException
    {
        m_webServer = componentManager.lookup( "WebServer" );
    }

    public void mountEar( String contextName, String pathToEar )
    {
        String[] warContextNames = getWarContexts( pathToEar );
        URL[] wars = getWarFiles( pathToEar );
        for( int i = 0; i < wars.length; i++ )
        {
            m_webServer.mountWar( warContextNames[i], wars[i] );
        }
    }

    public void unMountEar( String contextName )
    {
        // whatever
    }
}

Misconceptions

The following are worth stating:

  • You do not have to implement any Avalon or DNA interfaces to be reusable (wrap strategy) inside Loom.
  • Being Loom compatible is just as useful for whole servers as it is for small components.
  • Being Loom compatible can be for tools that are intended for client-side as well as server use.
Powered by Atlassian Confluence