Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Sunday, February 29, 2004

Automatic Proxy creation in Spring

Found an article on Automatic Proxy creation in Spring. Turns out that I'm not the only one troubled by the ease of gaining access to the "unproxied" versions of beans. Spring apparently has some "magic beans names" that get hooked into the object creation cycle, which allows all beans within an context to be proxied.

Of course, I still prefer HiveMind's approach: namespaces rather than magic names, and service creation and interception integrated together, with no cracks showing through.

HiveMind: element

I'm having one of those days ... as soon as I stray away from my computer, it's a big disaster. Messed up with the caulking gun and made a mess. Can't find the drill bits for my electric drill, so I can't mount the smoke alarm that's been sitting, for months, on the dining room table. And when I play on the XBox, I have two left feet. So back to the computer we go.

A common complaint about HiveMind, which cropped up again on The Server Side, was how much people hate <rules>. Yes, it's a simple, stack based language sitting in the middle of the document ... what's your problem? No one ever says "that's clever" ... it is usually something like stunned disbelief or just a total lack of comprehension. Since XML conversion is one of the tenets of HiveMind, it really should be simple and easy, so ...

I've now added (and integrated, and tested) an alternative: <conversion>. It accomplishes the same thing, but is both more limited, and more understandable and comprehensable ... especially in the most typical case, where you are defining largely flat data for a configuration point (or as parameters to a factory service). Even more so, since the default when using <conversion> is to use the smart translator.

In other news ... I'm conferring with Prashant tomorrow to see about re-submitting the HiveMind proposal to the Jakarta PMC. At this time, Christian Essl will have to be removed from the initial committers list ... he hasn't responded to any of my e-mails. That's a shame, he's provided some great ideas including the use of the nested proxies to handle the thread-safe instantiation of services without the need for synchronzing individual methods. Hopefully he'll return to the fold once we get started.

HiveMind: smart translator

Just added a new, nifty thing to HiveMind ... the smart translator. In HiveMind, a translator is an object that plugs into an XML conversion rule. It can interpret an XML attribute and convert it to a different type ... for example, to a boolean, or an int.

The new smart translator does the conversion based on the type of property the result will be assigned to. I got the idea from Spring, which does this kind of conversion already. What's the end result? Well, usage of the central hivemind.BuilderFactory is simplified, because now there's just a <set> element, the old type-specific elements (<set-boolean>, <set-int> and <set-long>) have been removed. Eventually, I would have had to write <set-double>, <set-char>, etc. That no longer necessary.

There's still specialized elements such as <set-service> and <set-configuration> ... those things can't be seamlessly accomplished using the JDK's PropertyEditor class, since they rely on a bunch of information only available inside the HiveMind Registry. Perhaps in the future, I'll be able to make the smart translator even smarter.

That covers the property injection side of things; I need to revisit the constructor injection part. There's still too many elements in place there, because with constructors, there's no property that's getting assigned, so no way to know what the type should be. In fact, I'll revisit the code and make it work backwards from the available constructors to find the correct match. In most cases, finding a match shouldn't be an issue, even if there's some guesswork with some of the constructor parameters.

Meanwhile, I have a bunch of other great ideas to implement, based on the recent discussions on The Server Side.

Saturday, February 28, 2004

java.blogs needs a little work

I read a lot of blogs via the java.blogs aggregator website, and the service has come a long way in terms of reliability in the last few months. Unfortunately, it has decided it can't read my blog any more. I've been furiously posting updates here and nothing shows up there.

The best thing is to bypass java.blogs ... I mostly use it to find blogs of interest that I then record as favorites inside my desktop aggregator: FeedReader.

Friday, February 27, 2004

Cutting down those dependencies

I've been somewhat troubled by the number of real, actual, runtime dependencies in HiveMind. I just put some work in, and managed to remove the dependency on commons-beanutils (and, by extension, commons-collections). I also removed code that invoke commons-lang's StringUtils.isBlank() so that HiveMind should be compatible with commons-lang 1.0. Those wacky commons-lang guys changes the meaning of method StringUtils.isEmpty() and introduced a new method, isBlank(), with the old behavior ... a lot of fuss for such a minor method, so I just re-implemented it as a method on the HiveMind class.

Anyway, my concern is, of course, DLL hell (and it's Java JAR equivalent), where widget A and widget B disagree about which version of widget C they each use. When you're in the business of hooking widgets together, this stuff is pretty critical. Less is more!

Interceptors in HiveMind and Spring

I've been doing more playing with Spring and HiveMind. Like most developers, I'm focusing on what I know and am comfortable with.

I wanted a simple example of using an interceptor. Now, I know Spring has a lot more AOP (Aspect Oriented Programming) goodies inside it, but again, one must start somewhere.

As a starting point, here's the service interface and class:

Adder.java:

package springex;

public interface Adder
{
    public int add(int arg0, int arg1);
}

AdderImpl.java:

package springex.impl;

import springex.Adder;


public class AdderImpl implements Adder
{

    public int add(int arg0, int arg1)
    {
        return arg0 + arg1;
    }

}

In Spring, you create a bean, often refered to as the target, then create a second bean that adds interceptors to the first bean. It ends up looking something like:

springbeans.xml:


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>	
  <bean id="adderImpl" class="springex.impl.AdderImpl"/>
	
  <bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
			
  <bean id="adder"	class="org.springframework.aop.framework.ProxyFactoryBean">
	
    <property name="proxyInterfaces">
      <value>springex.Adder</value>	
    </property>
			
    <property name="interceptorNames">
      <list>
        <value>debugInterceptor</value>	
      </list>	
    </property>
		
    <property name="target">
      <ref local="adderImpl"/>
    </property>
  </bean>

</beans>

The code for creating the BeanFactory and invoking methods on the bean is pretty simple:

SpringMain.java:

package springex.main;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import springex.Adder;

public class SpringMain
{

    public static void main(String[] args)
    {
        Resource r = new FileSystemResource("springbeans.xml");

        BeanFactory factory = new XmlBeanFactory(r);

        Adder a = (Adder) factory.getBean("adder");

        System.out.println("adder = " + a);
        System.out.println("result = " + a.add(4, 7));
    }
}

And when you run this ...

Debug interceptor: count=1 invocation=[Invocation: method=[public java.lang.String java.lang.Object.toString()] args=null] target is of class springex.impl.AdderImpl]
Debug interceptor: next returned
adder = springex.impl.AdderImpl@c8f6f8
Debug interceptor: count=2 invocation=[Invocation: method=[public abstract int springex.Adder.add(int,int)] args=[Ljava.lang.Object;@1b9240e] target is of class springex.impl.AdderImpl]
Debug interceptor: next returned
result = 11

You can see the Debug interceptor catching the invocations of toString() as well as add(). It doesn't do a great job on arguments. With some more work, you could create an adviser for the interceptor that would determine which methods to intercept, and which to leave alone.

Now, to do the equivalent in HiveMind. Because interceptors are the main approach to AOP in HiveMind, their useage is very streamlined:

META-INF/hivemodule.xml:

<?xml version="1.0"?>

<module id="springex" version="1.0.0">

  <service-point id="Adder" interface="springex.Adder">
  	<create-instance class="springex.impl.AdderImpl"/>
  	<interceptor service-id="hivemind.LoggingInterceptor"/>	
  </service-point>

</module>

The <interceptor> element references a interceptor factory service that fabricates an interceptor for the service. The factory is responsible for deciding which methods will be enhanded and how, by providing additional code that is invoked before and/or after the service methods are invoked on the core service implementation. You can have a whole stack of interceptors by adding additional <interceptor> elements.

One of the things I like is that there is no way in HiveMind to directly access the service implementation (the equivalent of the adderImpl bean in Spring). It will always be properly buried beneath proxy objects and interceptor objects.

Next, the main class.

HiveMain.java:

package springex.main;

import org.apache.hivemind.Registry;
import org.apache.hivemind.impl.RegistryBuilder;

import springex.Adder;

public class HiveMain
{

    public static void main(String[] args)
    {
        Registry registry = RegistryBuilder.constructDefaultRegistry();

        Adder a = (Adder) registry.getService("springex.Adder", Adder.class);

        System.out.println("adder = " + a);
        System.out.println("result = " + a.add(4, 7));
    }
}

And the runtime output:

adder = <SingletonProxy for springex.Adder(springex.Adder)>
springex.Adder [DEBUG] BEGIN add(4, 7)
springex.Adder [DEBUG] END add() [11]
result = 11

HiveMind does a careful job of converting method parameters into something readable, even when the parameters are arrays. It also shows the return value for non-void methods, and would log any thrown exceptions. The Log4J logger is springex.Adder. This is based on the service id, not the Java class.

Unless toString() is explicitly part of the service interface, HiveMind objects will implement their own version. Here, the object returned from Registry.getSerivce() is a singleton proxy, an object that handles the just-in-time creation of the core service implementation the first time a service method is invoked (and yes, its efficient and thread-safe). Its toString() implementation provides the service id and the service interface.

Folks, we have a Wiki

Erik Hatcher has spearheaded getting the Jakarta Tapestry Wiki up and running. He nagged. Apache infrastructure delivered. All rejoiced.

Thursday, February 26, 2004

HiveMind CVS is back up

You can now access HiveMind CVS again ... including all my latest changes (such as the change in package name). Follow the directions on the HiveMind home page.

Can HiveMind break its <rules>?

Inside the Server Side Discussion Opinion: Comparing HiveMind to Spring, Keith Donald pointed out a problem ... the <rules> mechanism is a real pain!

<rules> and all that is flexible and powerful, but also intimidatingly complex. In the discussion, I described an idea I expect to pursue: an alternative to <rules> for simpler, flatter data ... leaving <rules> in the background for when you need the industrial strength solution.

Meanwhile, I've been thinking that HiveMind's service models are slighly off. Currently, you define the service model with the <service-point>. However, the need for a non-standard service model (such as threaded or pooled) is really a function of the implementation not the interface. Therefore, I expect to move the model attribute out of <service-point> and into <create-instance> and <invoke-factory>.

HiveMind coming back to life

CVS is still not back up, but I've been able to update the HiveMind home page for the first time in months.

I expect the CVS to be brought back soon, at which point Prashant and I will submit an updated proposal for HiveMind to join the Jakarta project at Apache.

Meanwhile, my frustration with Maven has been growing. I'll probably bite the bullet in convert from Maven to Ant 1.6 + Forrest soon. Perhaps Erik will help?

Monday, February 23, 2004

Comparing HiveMind to Spring

I've started learning a little bit about Spring ... not just to make a comparison, but to look for proper channels for providing interoperability between HiveMind and Spring. I'm just starting through their documentation right now and will record observations as I go.

My intention here is not to bash Spring or start a flame war. I expect this posting to cause a bit of debate, and a chance for others to point out the inaccuracies that are certain to follow below. The final result will likely be a stand alone document, part of the HiveMind documentation. But you have to start somewhere.

Unlike a lot of open source projects, it looks like they have not ignored the documentation issue.

Yes, Tapestry right now gets bashed for bad documentation. That's because the documentation for 2.3 is actually very good, but the framework has jumped to the next release (3.0) and very little of the documentation has been updated. I've been, ahem, busy.

What follows are my observations as I work through the Spring documentation, starting with the Spring Reference.

Already, I'm trying to adjust to this new term, "Dependency Injection". That's Martin Fowler's term for Inversion of Control. To be honest, I think Mr. Fowler's term is a little short ... yes, the way your objects are connected together is a kind of "injection", but the rest of IoC is the Hollywood Principal (don't call us, we'll call you), which from my point of view is largely about lifecycle support ... when do objects get created and configured, how long are they used for, and how do you keep methods from being invoked under incorrect circumstances.

On the other hand, I'm likely to adopt the term collaborator when discussing different services that work together. Yes, it seems obvious once you've read it!

Back to the comparison. HiveMind's Registry seems pretty close to Spring's BeanFactory (or actually, as I realized later in the document, to Spring's ApplicationContext). They are both the ultimate containers, responsible for creating and configuring services and configurations (in HiveMind) and beans (in Spring). So far, as I'm reading, Spring appears to have different containers used in different contexts. By comparison, for services, HiveMind has different service implementation factories ... such as BuilderFactory and EJBProxyFactory (and, of course, you can roll your own).

This is a difference both minor and significant. Minor, because in the end run, you get to have services of different "flavors" ... are they beans or references to EJBs, or something else? Both HiveMind and Spring can define services (or beans). As I dig through the Spring documentation, I'll see just how well having this split works in practice ... for the moment, I think I'm on the right track with HiveMind to make the services heterogeneous within a single "context": the Registry.

The flip side is that Spring's bean factory is less tied to XML than HiveMind. HiveMind is very, very XML centric ... this is due to its configuration aspect, which is all about XML. And HiveMind is using XML in a very XML way, what with the HiveDoc (conversion of HiveMind module descriptors to a human-readable HTML format via XSLT). Spring has a specific implementation of BeanFactory, XmlBeanFactory, that defines the available beans using an XML format, but in Spring, this is pluggable. This is somewhat pluggable in HiveMind as well, since if you can create a ModuleDescriptor (perhaps from a database or somesuch) you can provide it to the RegistryBuilder for incorporation into the HiveMind Registry. But that is not the core intent of HiveMind ... the core intent is to read XML configuration files from META-INF/hivemodule.xml of all visible JAR files.

Spring can define a bean as a singleton or as a prototype. This roughly maps to HiveMind's service models. HiveMind has a richer set of these and uses a bit more runtime magic.

Spring's <bean> and <property> tags are roughly equivalent to the HiveMind BuilderFactory service. It appears that Spring does a bit more work to simplify property assignments ... for example, it does a bit of analysis of the types of properties to be set and automagically converts the string values (from the XML file) to the right type. I know that approach is more easily accepted by folks like Erik Hatcher. Of course, my attitude is that its always best to start strict and gradually ease up than to start loose and try and inject order.

Beyond that, HiveMind's and Spring's approach to IoC type-2 (set dependencies as properties) and type-3 (set dependencies as constructor arguments) are very close. Not a surprise ... only so many ways to skin a cat. However, Spring borrows a bit from PicoContainer, in that it can attempt to autowire collaborators by name or type. For all that, they're own documentation discourages the use of autowiring.

I wasn't sure what to make of Spring's dependency checking. Does it check to see that all properties of a bean get set? What if the bean's service methods look like properties?

So far, as I read the Spring reference, I can already see a point of difference. Spring will instantiate arbitrary beans. HiveMind is organized around interfaces, so when you define a service, you must also provide the interface for that service. The implementation, core service implementation in HiveMind parlance, is secondary, and may not even be provided in the same module. Advanced builder factories may mean that there isn't a particular class ... it may be brewed up at runtime, perhaps as a reference to something else (such as an EJB stateless session bean).

Spring has lifecycle interfaces that beans may implement to know when they are fully initialized, and when they are being disposed. HiveMind has effectively the same thing, plus an additional lifecycle interface that allows poolable services to know when they are returned to the pool. Spring includes a way, inside the XML, to specify a method to invoke (as an alternative to implementing a lifecycle interface) ... this is not a bad idea; I suspect it exists to support instantiating beans you didn't write, and therefore can't make implement the necessary interface.

In HiveMind, you would generally create a simple "shell" service around the collaborating bean to accomplish the same thing. A little more code, perhaps, but generally when you wrap an existing framework (such as Hibernate) as a HiveMind service, you'll define a new interface anyway.

Spring's BeanFactory has a concept of a configurer. The configurer is responsible for post processing the raw input for the BeanFactory (that is, the data parsed from the XML for an XmlBeanFactory). The example usage they give is to read a properties files and do substitutions of Ant-syntax references.

The generality of having multiple (?) configurers is cool, but if that's the only use, it is not as powerful and flexible as HiveMind's symbol sources. Symbol sources fulfill the same role(and even use the same Ant-derived syntax) , but give you a lot of power in setting up a whole list of places that symbol source data can come from at runtime.

Spring's PropertyPlaceholderConfigurer and HiveMind's symbol sources serve the same purpose: that some information needs to be determined at runtime: e-mail addresses, database URLs and so forth.

So far, I'm still confused about the lifecycle of beans relative to the lifecycle of the bean container. Does the bean container instantiate each bean when it starts up or (more likely) on first reference?

One concern I have for Spring with respect to collaborators is chain of creation. If bean A references bean B references bean C, then accessing bean A will result in B and C being created as well. Early HiveMind did the same thing, before I added in alternate service models and the idea of creating a service proxy to control just-in-time service creation.

Further, if C collaborates with A or B, you can get into a reference loop. The Spring bean lifecycle state diagram indicates that beans are created (via default constructor) first, then wired together second. So A, B and C would be created, then B wired to A, C wired to B and possibly A wired to C. Yes, a diagram would be nice here. However, this diagram and the reference to the default constructor doesn't seem to address what happens when using a constructor to inject dependencies on collaborators. It's a bit of a tangled problem, and easy to ignore ... it takes a particular degenerate case to hit a problem, but when that happens in production you are stuck!

Another advantage of the HiveMind approach (using a proxy) relates to co-dependent collaborators when using type-3 IoC. Using Spring or PicoContainer, it may not be possible to accomplish this ... you can't instantiate A by passing B into its constructor when you need an instance of A to pass into B's constructor. Under HiveMind, this is not an issue: A will be constructed with a proxy to B, and B will be constructed with a proxy to A. Order simply doesn't matter.

Ok, so a few of my observations are slightly off; BeanFactory is the bottom layer, and when you add Spring's ApplicationContext, you start to get to something a bit more like HiveMind, where multiple modules can work together.

Spring has, at this context level, I10N support similar to HiveMind's Messages support. It does support different locales simultaneously, whereas in HiveMind, the locale used for all messages is fixed when the Registry is constructed.

Spring's ApplicationContext provides some useful event interfaces; in HiveMind, the same functionality is available in standard services provided in the hivemind module.

Spring has code related to bean manipulation .. accessing named and nested properties. This isn't an issue in HiveMind but will be in Tapestry. Tapestry will continue to use OGNL for these purposes.

Spring works using JavaBeans property editors, so that it can translate property value strings to object values. In HiveMind, you would use translators (on attributes within elements within a schema) to direct how to convert a string to a property value and even do some validation.

Spring AOP appears to be something of a super-set of Tapestry's AOP (service interceptors). Both make use of interceptor proxies. In AOP terms, both weave around advice for object methods by creating a proxy at runtime. Spring can proxy classes whereas HiveMind can only proxy interfaces. It's generally accepted that programming to interfaces, rather than classes, it a better approach. In this area, Spring is giving developers a choice, whereas HiveMind is leading developers by the nose.

HiveMind's interception approach is built around services and service interfaces. The interceptor factory creates a proxy that implements an arbitrary service interface. So instead of saying "apply this advice to this method of this interface" you say "use this service to create an interceptor for this service". In HiveMind, the interceptor proxy will implement all the methods of the service interface. In theory, an interceptor factory could use a set of parameters to control how it is applied to a specific service and service interface. At this time, the capability is there but none of the built-in interceptor factories do that. Going forward, when we develop a Transaction interceptor factory, we'll start doing exactly that.

Originally I thought Spring AOP was about the same as HiveMind service interceptors (I wouldn't call service interceptors AOP, though maybe "AOP-lite"). As I dug further into the documentation, I realized that Spring's AOP is quite a bit more involved, though I suspect it falls short of the features possible with AspectJ.

It is possible to pass properties to a Spring proxy factory bean (the equivalent of a HiveMind interceptor factory service). Again, HiveMind can define a parameters schema, and pass arbitrary XML as the parameters.

HiveMind has the ability to allow multiple modules to contribute interceptors to a given service point. The upside of this is that new capabilities can be added to a service, such as service interface method logging. The downside is that, unlike Spring, we can't rely on the order in which interceptors are specified to be the order in which they should be applied to the service ... we need to add an order attribute for sorting.

Continuing with research; HiveMind has a concept of multiple modules; all modules are peers and can interoperate (contributing to each other). Spring has a container hierarchy, where (for example) a web application context will have child contexts for each servlet. Names of beans are resolved upward, such that the root application context can provide defaults for beans of a given name when not overridden by a servlet configuration. HiveMind has a single level of services (service ids are unique), but using a namespace approach (the qualified id for a service incorporates the unique id for the module ... thus services with fully qualified ids such as hivemind.BuilderFactory or com. myco.myapp.BusinessLogic).

Remember that HiveMind is a services and configuration microkernel. This is the raison d'etre of HiveMind: complementing and integrating a service model with a distributed configuration model ( inspired by the Eclipse plugin model).

Summary:

Concept Spring HiveMind
Inversion of Control (Dependency Injection) Types 2 and 3
(Property setting and by constructor).
Can autowire.
Types 2 and 3. Explicit wiring only.
Multiple modules Yes, hierarchical. An application context may have children. Yes, flat but namespace qualified.
Aspect Oriented Programming Very rich; standards based. Built in ways to target particular methods. Limited to "around" method introductions on interfaces.
Service management Manages singleton beans (or one-shot "prototype" beans). Bean implementation must be provided with bean definition. Many service models, including singleton, threaded and pooled. Service implementations must implement service interface. Service model hidden behind a proxy object, which may be shared between threads. Service implementation may be contributed by different module that service-point definition.
Configuration information Can configure beans only. Bean definition is single place (single XML file). Very rich; configuration-point to compliment service-point. Supports multiple contributions from many modules. Conversion of XML contributions into Java objects. Proxies for just-in-time access and conversion.
Project Documentation Print out the XML? HiveDoc: rich HTML (like Javadoc) describing all services and contributions across all modules.
Integration Integration with Hibernate, JTA, Spring MVC, more? Planned integration with Tapestry and others -- but nothing yet!

Sunday, February 22, 2004

Back from Philly

Just got back from No Fluff Just Stuff and had a really good time. It was a true whirlwind -- flew in Friday night, presentations on Saturday (8:30am and 3:30pm), plus time on the expert panel after lunch. I attended Bruce Tate's sessions on Hibernate and J2EE vs. .Net. Last night was dinner with the whole crew. When NFJS comes to the Boston area, I'll have to get people to a decent restaurant, maybe even a Chinatown excursion.

This seminar was small even for a No Fluff, only seventy five attendees total. Turnout for the Tapestry sessions was a bit disappointing. It was actually the smallest crowd I've ever given the Tapestry spiel to: 5 in the first session I believe, 7 in the second. Bruce and folks said they often get questions about Tapestry during the expert panel ... but not while I was there. Interestingly, when I announced that I was the creator of Tapestry, heads on the expert panel turned ... they thought I was just the author of the book.

I need to tune my presentations a little ... there's a bit too much marketing ("fluff") in them, and I should use the time to cover some more interesting features (such as form input validation) ... that is, more "stuff". There's no point in selling Tapestry to this particular crowd ... they're already somewhat sold in choosing my session over the other three running at the same time. Even so, I got back a lot of positive evaluations. The one bit of Fluff I should add in is to discuss the other members of the Tapestry team, the subject of the most common question between sessions ... a disguised way of asking "how viable is Tapestry?".

I think I'll change the titles, too. Beginning Tapestry: Java Web Components is too generic. Expanding that session to discuss form input validation will show off the power of the framework, but also allow me to change the title to something like Build Powerful Web Forms with Tapestry, which is more in the spirit of NFJS.

With the work of the book finally behind me, I'm beginning to see other things more clearly. I really have been stressed, especially the last few months. One question that came up during the expert session concerned time management ... I stayed quiet because for the last year, I haven't managed my time, the book has managed me. Strangely, despite learning Maven and creating HiveMind, I still don't feel I've kept adequately current ... thus my renewed interest in Spring and Hibernate.

With HiveMind now free, I can put together a session about it. I think an important part of the presentation will be interoperability ... my live demo can show things like accessing a session bean in JBoss as if it were a HiveMind service. I also want to learn a bit more about Spring. What I want is the ability to represent beans managed inside Spring as HiveMind services, plus whatever else is necessary to allow HiveMind to run Spring within itself. I'm sure the Spring guys can do just the opposite (get a HiveMind Registry created as a Spring managed bean).

Also, I'm going to finally take some time to look into Hibernate properly. I'll certainly be doing some Tapestry / HiveMind / Hibernate (Hivernate?) integration. In fact, showing how to use Hibernate properly within a Tapestry application would make another great session for NFJS.

Friday, February 20, 2004

Tapestry In Action -- Printing on Tuesday

Tapestry in Action will be printed on Tuesday, Feb 23 2004. Physical copies of the book will arrive at my house in mid March, at the same time copies arrive at the distribution warehouse. Copies will arrive in book stores, and start shipping from online retailers such as Amazon, about two weeks after that (late March or early April). I'm doing the final, final, final proofreading today, before I split for the Java Freedom Software Symposium.

Thursday, February 19, 2004

HiveMind developers ... Please check in!

Four months ago, we assembled a list of developers interested in forming the core committers for HiveMind when it went into incubation at Apache. Because of the WebCT IP issue, things have been dead quiet since then. Tomorrow morning (USA East Coast time), Prashant will be faxing the software grant for HiveMind to Apache. Once that is received and acknowledged, Prashant will be resubmitting the proposal to the Jakarta General list.

Part of that proposal is the list of initial committers. If you are still interested, please get in contact with me! This isn't a cattle call ... I'm just having trouble reaching a couple of names on the list via e-mail and am hoping those folks monitor this blog.

In addition, you need to sign a Contributor License Agreement (CLA) before you can be given commit access to the Apache CVS servers.

Wednesday, February 18, 2004

HiveMind grant is a go!

Just got an e-mail from Chris Vento, the CTO at WebCT. He's signed the software grant for HiveMind, so we can finally get back to work on it. I'm checking with Prashant Nayak (also at WebCT) to see what's happening there ... I think it's just a matter of Prashant faxing the signed document to the ASF.

HiveMind and event listeners

I'm still very confident that the HiveMind debacle will end, and soon, with the software grant to the ASF.

Confident enough that on my trip to and from San Jose, I opened up the HiveMind source code for the first time in months to add some new features (even with a flakey 'A' key).

It seems to me that a very common use case for HiveMind services is for one core service implementation to register for events produced by another service. In terms of Tapestry, I can imagine services that need to know when the reset service has been activated, so that they can clear their caches.

In alpha-3, you could have the BuilderFactory set a property of your service to an event producer service, then inside initializeService(), you could add this as a particular listener of the producer.

Kind of ugly:

  • You need a property of your core service implementation to store the event producer service, even though you only use it the one time
  • You need to implement Initializable just to do the event registrations

The changes I just made affect BuilderFactory. It adds a new element that can be nested inside <construct>: <event-listener>. <event-listener> allows you to specify another service (using a service-id attribute). This other service is the event producer, which includes methods in its service interface for adding (and removing) listeners of a given type. The constructed service will be added as a listener of events from the event producer service. Just the core service implementation needs to implement the listener interface ... the service interface does not have to extend the listener interface.

The thing to keep in mind with this is that HiveMind is using JavaBeans definition of an event set to find the method to invoke. An event set is to listener interfaces as accessor methods are to a property. An event set is a pair of methods:

  • public void addFooListener(FooListener);
  • public void removeFooListener(FooListener);
(defines an event set named 'foo'). You need both the add and the remove method for the JavaBeans Introspector to recognize the event set.

I'm very happy with this code ... it's another sweet layer of abstraction that will allow all manner of services to quietly interoperate behind the scenes. I can already picture many uses for this in Tapestry 3.1.

This Blog brought to you by the letter 'A'

During my lightening trip out to California yesterday, I had another horrible infrastructure let down: when I got to my hotel, the 'A' key on my laptop keyboard was damaged. No clue why ... it had worked in Phoenix (the stop over on the way to San Jose) but there it was ... the feel of the key was wrong: it needed excess pressure to work, and even then, didn't always work. I had visions of me having to send the entire thing back to Dell for service ... possibly weeks without my laptop!

Fortunately, I follow the principal of WWDGD? (What Would David Goldman Do? -- David being one of my best friends and exceptionally handy at all things electronic or mechanical). David would have bravely pulled up the broken key cap and figured out what was wrong underneath. That's exactly what I did ... and found an anomalous amount of hair in there. Anyway, a careful cleaning, a gentle re-application of the key cap ... and the letter 'a' is back. Useful for words such as 'Tapestry'.

Sunday, February 15, 2004

Garbage collection and performance

A nice article over a IBM's DeveloperWorks: Java theory and practice: Garbage collection and performance

Tapestry went through this same kind of work over back in the 2.0 timeframe (around the time of JDK 1.3). I went to a lot of trouble to pool all sorts of little objects, particularily StringBuffers. Guess what? Performance went down! I knew this because I did some profiling. I stripped all the pooling code out and, even with all those short term instantiations of StringBuffers and the like, performance went back up. Later JDKs have helped even more.

There are too many moving parts in a Java application (too many frameworks, too many layers of too many different kinds of code, too many threads) to predict how performance will be affected by your code changes. As the IBM article points out ... you can easily hurt more than you help.

Wednesday, February 11, 2004

Source code for "Tapestry in Action"

I've gotten a few requests for the source code for Tapestry in Action. For some reason, Manning hasn't made it available yet on thier web site.

As an interrum measure, I've put it up on my personal web site: TapestryInActionSource.zip. Enjoy!

Tuesday, February 10, 2004

Thanks again for Spindle, Geoff!

So, I'm putting together new examples to support my upcoming presentations and this time around, I'm finally getting to use Spindle. It's very impressive ... it takes the normal build/deploy/execute/analyze cycle (the analyze step being the time you take to figure out what caused the exception page to display) and reduces it to a build cycle most of the time. The kind of errors I make all the time (wrong attribute name on an element, mismatched component ids in the template and the specification, missing required parameters on components, and so forth) are caught at build time and flagged like any other kind of error.

Here's a really huge picture that shows just a few of Spindle's features. It's no wonder that Spindle was voted the top Eclipse plugin recently, it's a compelling reason to use Eclipse in the first place ... just as Tapestry is a compelling reason to use Java.

Friday, February 06, 2004

HiveMind relaunch draw near

This time, I gave Prashant a rest, and went right to the top: Chris Vento, CTO of WebCT and the man backing the decision to go open-source with HiveMind.

Where-as Prashant could only guess at what was happening, Chris could be very specific: "I will move on it by next week and get the lics form executed and submitted." The reality has been a crush of work, deadlines, end-of-year reviews, client contracts, and so forth--not any desire to back away from releasing HiveMind as open source.

I'm looking forward to this; I have a bunch of powerful features for HiveMind planned, including a whole suite of JMX related stuff and it's all been on hiatus waiting for this.

In other news ... Noel Bergman found out what was causing the e-mail snafus at Apache for the last week or so. Surprisingly, it was on the Apache side, not the Comcast side! A temporary filter was put in place because of the MyDoom virus that was inadvertently left on. Noel has changed a daily summary report to include details about such things ... hopefully it won't happen again.

Wednesday, February 04, 2004

Sending smoke signals ... voted off the Island by Comcast Apache

It can get a little frightening when you realize that you've taken for granted a technology and it suddenly fails to work.

In my case, it appears that there's a disruption which is preventing mail originating at comcast.net's domain from reaching apache.org's. This may have been going on for a week (its probably related to the massive amount of e-mail from the MyDoom virus).

I'm starting to try and penetrate comcast's protective walls of low-level flunky customer support. Basically, if you are a comcast customer, no mail sent to apache.org (including all the mailing lists) is getting through.

That means, for me, that a bunch of question responses on the mailing lists haven't gone through. I started a vote to release Tapestry 3.0-beta-4 that is in limbo. My business cards have the hlship@apache.org e-mail address (forwarded to hlship@comcast.net) so Comcast customer can't reach me directly.

I may have to setup a Yahoo account, just to be able to get the attention of the Apache infrastructure folks. What next ... no dial tone?

Update: It was Apache's fault after all!

Tuesday, February 03, 2004

Java Freedom Software Symposium by No Fluff Just Stuff

I'll be presenting two sessions on Tapestry at the Java Freedom Software Symposium by No Fluff Just Stuff, which takes place Feb 20-22 in Philadelphia, PA.

I'm pretty excited by this ... not just the chance to do another big Tapestry presentation, but the chance to attend the other sessions (many of which are things I'm dying to get into) and meet the other speakers. I expect to learn a lot ... a lot about other Java open-source technologies and a lot about presenting (these guys are veterans).

Hope to see a few of you folks there ...