In a previous entry, I mentioned our intention to use the Spring 2.0 custom schema support to simplify the use of our Sprung service libraries, when they are called from Sprung clients. In this entry I will describe the complexity that we’re trying to tame and show how we can achieve this from the perspectve of the client code, using a service-specific schema. In a later entry I will describe one way to simplify even how the service code provides that schema.
Before [tag]Spring[/tag] 2.0 we had to lay down internal standards to offset some Spring-introduced complexity that we encountered in these circumstances. These standards were designed to allow clients to access the Spring configuration files of all its service libraries (avoiding bean name collisions) and make it clear to the developers of the clients which properties of which beans defined in the service layer could or should be set or overridden in the client’s Spring configuration files. (Note that the alternative – doing all the configuration in the client – is not workable pre [tag]Spring 2.0 [/tag]. We cannot reasonably expect the client developer to know how to configure all the plumbing of every service it uses. Even if we did, the result would be configuration duplication – the same unchanging bean definitions spread over every client that reuses a service.)
Standards work best when supported by the development toolset, but Spring’s total flexability in configuration – normally an asset – becomes something of a liabilty here. We can only offer service and client configuration templates to guide developers. After that, there are no clear signposts to tell them when they are taking a wrong turn.
To summarise this complexity: Service developers have to try to name their service beans and the files in which they are defined, in a unique and predictable way. Client developers have to reference the configuration files of all the services they use, to distinguish the service beans that they need to retrieve and use from all the plumbing, and to figure out what other beans and properties must be defined by the client configuration in order to make the service actually work.
The introduction of custom schema in Spring 2.0 changes all that. This new feature makes it possible to define custom xml configuration formats, backed up by xml schema definitions (XSDs). The Spring framework now comes with hooks to translate these custom tags back into Spring beans. This has two major effects:
- Reduced verbosity of configuration files.
- A distinction is made possible between public and private elements (beans and properties) in configuration files.
These two factors combine to allow us to change the way we manage the client-service Spring configuration relationship. We can now move the configuration out of the service layer and up to the client. (As we will see in a moment, only the beans we decide to make ‘public’, and the properties we allow to be set or overridden, are configured on the client layer, but it is certainly the case that the service configuration files are no longer read by the client.)
What difference does this make?
For the developer of the client code, there is a big difference. Here’s an example of a clientside Spring configuration before Spring 2.0. Note the conventions and assumptions that have to be followed in order to correctly wire in the service:
<!-- The this bean's name is set in the service Spring config -->
<!-- The service bean name is set in the service Spring config -->
So the client has to know quite a lot about what’s going on in the service’s Spring config file. And what’s more, it has to know where to find that file. One way of doing this, using the example of a web application client, is to put something like this in your web.xml:
This is quite a simple scenario where just one service is being used by the client code, and where only one service configuration file is present.
After Spring 2.0, here’s what it can look like if the service defines its own custom schema:
<ts:testservice name="myTestService" dataSourceRef="myDataSource"/>
<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
Hang on! That’s more verbose than the previous example! I thought that one of the the points of all this was to reduce complexity?!
It is. But the complextity for the client developer lies not in the number of configuration lines, but in learning what must be configured to harness the service. Here’s how the reduction in complexity is achieved in this example.
- The developer of the client uses an xsd file published by the developer of the service. With one simple tag, the service bean is added to the container, given a name chosen by the client developer, and associated with a reference to a datasource (again named by the client developer). This tag replaces any of the service layer Spring config files which the client developer was obliged to find and include in its application context.
- Any xml editor worth its salt will use the xsd file to help the client developer fill out the fields of any given service tag. This XSD is the best kind of documentation that any API designer can provide.
- Client developers can name the service bean instances as they like, without having to look up the name defined by the service developer, and without having to manage name clashes between multiple services.
The more services the client uses, the smaller the percentage overhead of the extra lines in the client configuration.
For the price of a few extra lines of configuration, the client developer can take more control over the use of the service API, and doesn’t need to spend time guessing what the client’s configuration responsibilities are.
There are problems here though. The amount of work that the service developer has to do to provide this simplicity (the translation of these simple client-side tags into all the beans that go to make up the service) is significant. In the next entry I’ll take the service developer’s point of view, showing what needs to be done to expose a service through a custom [tag]Springframework[/tag] xml schema. Importantly, we see how much of this work can be generalized into reusable utility classes (hint: a lot!).
– brendan lawlor –