The Next Big Component Architecture

Looking back a little

A few years ago [0], the web application world was a mess. We had servlets [1], which involved embedding html inside java code, and we had jsps, which involved custom markup that was just as ugly. The business component world was just as messy, with EJB 1.0 out [2] but very little actual support and buggy, pricy servers. We also had applets which allowed us to circumvent all that ugly stuff by circumventing the entire http/html layer and writing ugly binary socket-based protocols. The latter got unpractical as netscape lost the browser war and Swing arrived on the scene too late.

Still, there was a good JDK [3], including one for linux, and the JFC were shaping up pretty nicely. We jumped to java en masse. Booming business.

“This is a mess. Lets fix it.”

So we had JServ 1.0, JBoss 1.0, something called “tomcat” no-one actually used, lots of crappy code, and not much potential for reuse. Several lessons were learned in developing all of that stuff, and several projects formed all around the open source world that fixed various pieces of the puzzles and scratched various itches.

Lessons learned

Let’s look at some of the things we’ve figured out along the way….

  • Java works: Well duh! It was clear to many that there was a good base platform here that got most details reasonably right (even if Stack inherited cruft from Vector).
  • XML works, too: A, the beauty of a simple markup language both machine-readable and human-readable!
  • language mixing sucks: embedding markup language or scripting language inside inside programming language quickly becomes unreadable. Parsers or templating technologies make life easier.
  • decouple: I won’t seriously consider programming big applications in a language that doesn’t have the keyword ‘interface’ these days
  • big design is bad: one acronym – EJB. Ugh.
  • open source wins: in 2003, almost every important component for building server side software is open source. Except for a JDK, for some reason.
  • unit tests: More Important Than Interfaces. The various parts of XP methodology are slowly saturating the java world.

…and one could go on and on. The landscape is still improving.

Sharing Is Hard

Look at the number of “web application frameworks” out there. Dozens of them. Look at the lack of common ground they have. Missed potential. It takes an extreme amount of energy to figure out a way to encapsulate some basic functionality so that it can successfully be shared even across a limited problem domain.

One of the things avalon is supposed to enable is reuse within a problem domain, or even across problem domains. However, we have not succeeded in making this so easy and obvious that the average java bloke can figure out in an afternoon how to do it. Sharing remains hard.

In practice, the only clean form of sharing occurs on a large scale is when someone succeeds in totally isolating a cross-cutting concern into an appropriately partitioned vessel. Like log4j.jar or JAXP, or GUI javabeans. These are the exception rather than the rule, especially as partitioning grows.

There are exceptions, where inferior or all-encompassing tech still manages to become a standard, usually grown out of a standards body (DOM) or corporate backing (EJB), but those usually get competition from people who’ve scratched their itch and improved on the concept (JDOM, EOB).

So, some statements summed up

The java platform works, but frameworks like avalon remain complex, too complex for the average joe user (or the average professional with only a day or two to spend [5]) to actually gain critical mass without corporate backing. Therefore, basic software architecture is still being re-invented on a daily basis and sharing remains very hard.

The winning concepts

  • IoC
  • COP
  • Api/Impl seperation
  • KISS
  • XP, test first
  • asynced events
  • pipelining
  • aspects
  • attributes
  • scriptability
  • pluggability
  • strong typing

Avalon has IoC, COP, Api/Impl seperation. Attributes are coming. Various stuff is easily added. The main thing it misses is KISS. The other main thing is pluggability. Avalon is not pluggable. Components written using avalon are pluggable inside other components written using avalon. Applying avalon to an existing system often requires a big overhaul and lots of refactoring. Applying avalon to an existing programmer takes a week at least.

Avalon4 Just Doesn’t Cut It

I’ve been slowly forming this opinion over several months of looking at many many avalon-related technologies. Take a look at phoenix, ecm, fortress, microcontainer, tweety, merlin, plexus, eob, guiapp, cocoon. Each and every one of these requires too much work for every component you write because there are so many strict contracts. Contracts are good, but they need to be intuitive, too.

The challenge

Lose the baggage. Make it simpler. Make it smaller. Encapsulate the dynamism in a single place. KISS. Yes, Paul & friends figured it out already [6].

The Next Big Component Architecture will be about Small Architecture.

What it might look like

Here’s a not-attribute-aware, jython-scripted, api/impl seperated, IoC/COP, aspect-oriented, strongly typed bean. It is also a regular javabean that can run in any simple java2 environment.

Parser.java

/** a basic work interface */
public interface Parser
{
    Node parse( Source source );
}

SomeParser.java

/**
 * a basic bean with some system-level needs (logging), some
 * configuration requirements and a dependency on another
 * component.
 */
public class SomeParser implements Parser
{
    private Log m_log = null;
    private boolean m_validate = false;
    private XmlReader m_XmlReader = null;

    //////////////////////////////
    /// work interface: Parser ///
    //////////////////////////////

    public void parse( Source source )
    {
        m_log.debug( "source url: " + source.getURL() );
       
        /* ... */

        if( m_validate )
            validate( source );
        /* ... */
    }

    //////////////////////
    /// required stuff ///
    //////////////////////

    public void setLog( Log log )
    {
        m_log = log;
    }
    public void setValidate( boolean validate )
    {
        m_validate = validate;
    }
    public void setXmlReader( XmlReader reader )
    {
        m_XmlReader = reader;
    }

    ///////////////////////
    /// utility methods ///
    ///////////////////////

    protected validate( Source source ) { /* ... */ }
}

MyAppDefaultInit.py

# a common jython script that adds basic custom configuration
# support, AOP interceptors and an event interceptor

instance.log = container.getLog( config.attrs.name )
instance.log.logLevel = config.elems.log-level

# time method calls
if( config.elems.instrument )
  interceptors.add( method="*", on="preCall",
    action="context.parse.startTime=System.currentTimeMillis()" )
  interceptors.add( method="*", on="postCall",
    action={
      context.parse.endTime=System.currentTimeMillis()
      timeTaken = context.parse.startTime - context.parse.endTime
      properties.log.debug( "time taken for " \
          + currentMethod + "call: " timeTaken )
    } )

# enable hot reconfiguration
interceptors.add( event="reconfigure"
    action={
      instance.log = container.getLog( event.config.attrs.name )
      instance.log.logLevel = event.config.elems.log-level
    } )

SomeParserInit.py

# a class-specific jython script that extends the behaviour from
# the above script

container.call( "MyAppDefaultInit.py" )

instance.xmlReader = container.getComponent( "xml-reader" )

# update validate property before parsing to allow for
# runtime reconfiguration
interceptors.add( method="parse", on="preCall",
    "instance.validate=config.elems.validate" )

# enable hot reconfiguration
interceptors.add( event="reconfigure"
    action={
      instance.validate = event.config.elems.validate
    } )

WEB-INF/components.config.xml

# a sample xml configuration file, might be passed in using
# configure( Configuration conf ) or configure( Node node )
# or by using a jython script
<components>
    <defaults>
        <config>
            <instrument>false</instrument>
            <log-level>DEBUG</log-level>
        </config>
    </defaults>
    <component name="xml-reader"
        class="org.sf.xml-utils.XmlReader"
        lifestyle="threadsafe"/>

    <component name="parser"
        class="SomeParser"
        lifestyle="pooled">

        <config>
            <validate>true</validate>
            <instrument>true</instrument>
        </config>
    </component>
</components>

MyBean.java

/**
 * doesn't care about configurations, scripts, or whatever,
 * but understands what setXXX() implies.
 */
public class MyBean
{
    private Parser m_parser;

    public MyBean()
    {
        m_parser = new SomeParser();
        m_parser.setLog( new SimpleLog( "parser" ) );
        m_parser.setValidate( true );
        m_parser.setXmlReader( new XmlReader() );
    }
    public void doStuff()
    {
        m_parser.parse( /* ... */ );
    }
}

MyComponent.java

/**
 * client component; similarly might have an init
 * script and/or configuration
 */
public class MyComponent implements Something
{
    private Parser m_parser;

    public void setParser( Parser parser )
    {
        m_parser = parser;
    }
}

WEB-INF/plugins.conf.xml

<plugins>
    <plugin id="improved-pool" version="1.1-dev">
        <config>
            <initialSize>5</initialSize>
        </config>
    </plugin>
    <plugin id="jelly-scripting-support" version="1.0"/>
    </plugin>
</plugins>

$PLUGINS/improved-pool-1.1-dev.jar/plugin.py

instance = new ImprovedPoolManager()
instance.initialSize = config.elems.initialSize
container.pool = instance

$PLUGINS/jelly-scripting-support/plugin.py

instance = new JellyScriptManager()
container.addScriptingSupport( instance, "*.jelly" )

References

[0] – http://http://java.apache.org/
[1] – http://java.sun.com/products/servlet/index.html
[2] – http://java.sun.com/pr/1997/dec/pr971210-01.html
[3] – http://java.sun.com/products/jdk/1.1/index.html
[4] – http://java.apache.org/
[5] – http://jeremy.zawodny.com/blog/archives/cat_java.html
[6] – http://www.picocontainer.org/

(an archived e-mail sent on June 23, 2003 to the Apache Avalon mailing list)