One of the things that really bugs me is when people go about reinventing configuration, discovery, or wiring for their Java components. We have plenty of containers out their which can do this for us. By reusing them we end up with higher quality code and can focus more on features specific to our problem domain. Spring has been the most popular container for a while now, and its pretty obvious how to do configuration or wiring together of components. But how do you do discovery?
I’ve seen many examples which advocate this:
<bean id="widgetManager">
<constructor-arg>
<list>
<ref bean="widget1">
<ref bean="widget2">
</list>
</constructor-arg>
</bean>
The problem with this though is that you need to explicitly list all your widgets in your configuration file. In many cases what you’d like instead is to be able to just add acme-widget.jar to your classpath and have Spring automatically pick it up!
To get around this issue in CXF I wrote a SpringBeanMap class. What this will do is look through all your ApplicationContexts and find beans which implement a specific interface. Here is a small example of how it would be applied to the above case:
<bean id="org.widgetthingy.WidgetManager">
<constructor-arg>
<bean class="org.apache.cxf.configuration.spring.SpringBeanMap">
<property name="type" value="org.widgetthingy.Widget"/>
<property name="idsProperty" value="widgetIds"/>
</bean>
</constructor-arg>
</bean>
This will invoke the getWidgetIds method on any bean which implements the Widget interface. For each id supplied, it will use it as the key in your Map with the widget as the value. Once that is done the resulting Map gets supplied to your WidgetManager via a constructor or property.
That only solves half the problem though. We also need to discover new configuration files that have been added to the classpath. This can be done by creating a ClassPathXmlApplicationContext that looks for all the **/widget-*.xml files. Now all one has to do when writing a new widget is define their bean in a widget-foo.xml file and it will be automatically added to your application..
The only issue that I’ve found with this approach is that the Widgets aren’t lazily loaded. This isn’t a huge deal for us at the moment as the things we’re loading aren’t resource intensive. There is at least one way around this for those so inclined. Instead of instantiating the bean and invoking getWidgetIds() we could instead look at the Spring BeanDefinition for what the “widgetIds” property holds. This means that you must include the widgetIds in your Spring bean definitions though, and they can’t be hard coded into your class.
If anyone has other approaches which get around this, I’d love to hear them. I’ve recently re-enabled comments so drop me a line…
UPDATE: Arjen Poutsma pointed out via email out that if I want lazy initialization there is no other possiblity than to put the ids in the XML. If I want to get ids via the bean, I’m going to have to instantiate it. (*kicks self*) I updated SpringBeanMap to support both methods. Now if there are ids specified in the XML they are used and the bean is not initialized. If there are no ids specified, we initialize the bean and grab them. I’m fairly happy with this solution now. It is completely non invasive and very flexible!