Eric Pugh wrote:
Hi all,
Over the past 6 months I have been really getting into the COP approach. Starting with Excaliber, a brief foray over to XWork, and now the porting of Turbine and Fulcrum to Merlin. And over the past month's I have had to explain repeatedly why I am interested in using a container and components in my applications.
And most of the ususual reasons make sense to people. The easy configuration of components. The reliance on the container to start things up in the correct order, etc..
And of course, the number one complaint/concern: "I don't want to implement somebodies interfaces.. Isn't that like EJB?" To which I typically respond that implementing a Startable/Stoppable/Configurable interface is no different then what you would be doing anyway if you needed that functionality. And you are implementing well understood, widespread interfaces. Especially the Startable/Stoppable interfaces, that makes complete sense. Some people people do point out that instead of a Configurable interface, they would set a series of javabean properties. To which I respond that eventually, if you have too many, you end up with a config object, so you might as well start out with Configurable. And you can always do it by hand via JavaBeans. And this satifies most people..
Just a point concerning "I don't want to implement somebodies interfaces.."
I think this is a totally valid concern. As soon as you implement someones interface you create a dependence on the interface which means a dependence on the package which means a dependence on the project which means a dependence on that community.
Instead - we can look at the container <--> component contract with a little more flexibility and say - ok - what is you strategy for configuration? We (Avalon) can provide a default strategy using the Configurable interface and Configuration artifact - and if you don't like that, then you can declare an alternative strategy using your own lifecycle stage handlers. What this means is that a developer can eliminate any Avalon dependency from the component, providing the same developer provides a set of custom handlers. This means that the developer completely isolates the component model contractual dependency to a small number of custom extensions. Its more work for the developer - but in some cases its justified.
All of this is possible today.
Which brings me to the one thing people really don't get.. Why do I have to implement Serviceable to get a hook into other components? Why can't the container just give me the components? I mean, in my java code inside the component I have to pick what I want. Why not define it outside and have the container set it? And I get all balled up in explanations.. Of which I can think of many, like at run time I can pick and choose my components I need.. If I have setting X, I get component X. If I have setting Y, I get component Y. However, this did kinda make me wonder... Why can't I just expose the components I need and provide a setter? Most of the time, I always want an instance of component X. In fact, often, because of unit testing, if I have a dependency on component Y, then I just provide a setter, so that at unit test time I can pass in my MockY component, versus having to use a custom test configuration loaded through avalon.
I looked at the /tutorial/composition/application/impl/../Application.java class, and it does precisily what I want it to do.. It implements Serviceable, and it looks up objects named "locator" and "publisher". Admittedly, in the example, it then does something, but for my purposes, I would be happy to just know that when I loaded up the Application component I had a locator and publisher objects. Now, both of these objects were defined for me in my block.xml:
<include name="location" id="merlin-tutorial:locator-impl" version="1.0"/> <include name="publisher" id="merlin-tutorial:publisher-impl" version="1.0"/>
Including all the info to resolve them! So, what would be involved in having a something that did the servicable for me? If Application.java has a setLocation() and setPublisher() methods, and something was set in the block.xml, then Application.java could skip the serviceable method and Merlin could just populate the setters for me. In the XML below, I just added an autoset="true" to the component declaration that would be the key to Merlin to just create the darn objects and pass them in...
<container name="application"> <classloader> <classpath> <repository> <resource id="avalon-framework:avalon-framework-api" version="4.1.5"/> <resource id="avalon-framework:avalon-framework-impl" version="4.1.5"/> <resource id="merlin-tutorial:locator-api" version="1.0"/> <resource id="merlin-tutorial:publisher-api" version="1.0"/> <resource id="merlin-tutorial:application-impl" version="1.0"/> </repository> </classpath> </classloader>
<include name="location" id="merlin-tutorial:locator-impl" version="1.0" /> <include name="publisher" id="merlin-tutorial:publisher-impl" version="1.0"/>
<component name="application" class="tutorial.application.Application" autoset="true"> </component>
</container>
I can see issues where location maybe wanted some other objects as well. However, those dependencis could be setup in here to, correct? Via more <component> tags?
The reason I bring this up is because I have been reviewing, under prodding, other frameworks. The nice thing about the SpringFramework is that your components don't have to implement interfaces (other then a startable/stoppable one) to be used in Spring. And they don't have to implement a Serviceable type interface.
What you describing is an alternative strategy for the assignment of
services to a component. Lets' forget about meta-data directive like autoset,
but instead think about how to declare an alternative assignment strategy at
the level of a component type.
E.g.:
/**
* Assignment of the service by the container.
* @avalon.dependency
*/
public void setRabbitWarren( RabbitWarren warren )
{
m_warren = warren;
}It seems to me that there is sufficient information for a container to resolve this.
I think having Serviceable is very good in some situations. But, in many, it is overkill. I just want to get a component, and have the X dependent component already loaded, versus having to everytime do this:
LocationService locator = null; Object object = manager.lookup( "locator" ); if( object instanceof LocationService ) { locator = (LocationService) object; } else { final String error = "Object " + object.getClass().getName() + " does not implement the LocatorService class."; throw new ServiceException( "locator", error ); }
If you using Merlin you don't need to do all of this. You only need to do the following:
LocationService locator = (LocationService) manager.lookup( "locator" );
All of the checking has already been done before your object is even instantiated.
On a related note.. If you had this facade (?)/interface layer, then the various Activity lifecyle interfaces wouldn't need to be implmented. I could instead provide a mapping file that said: For my component, I do implement the Startable interface, I just didn't implement it, and my method is called "begin()". This would work for noarg methods especially well. Actually, I guess would this be a proxy?
Sorry - I don't understand the above para. Every lifecycle interface in Avalon is optional. In fact is perfectly ok to have an component that implements no avalon lifecycle interfaces. I figure I'm missing something - can you expand?
Cheers, Steve.
Eric Pugh
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
--
Stephen J. McConnell mailto:[EMAIL PROTECTED]
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
