writing an osgi service in a web-app
I’ve written some notes trying to understand and explain from a management perspective why service oriented architectures works, as a motivation to understand the Resin 4.0 architecture. To make the ideas concrete, the end of the post shows the bare-bones hello world example for an osgi/web-app service.
While services can improve application organization, they can also improve the programming team coordination. From a management perspective, services organize the application around staffing resources, and organize team communication. Since the PHBs often can’t keep track of the engineering details, services also let engineers explain to management what they’re working on.
Organizationally, services can improve team communication because each service defines a programming boundary between team members. Like a legal contract which defines the requirements and responsibilities between two businesses, a service clarifies the expectations of the service developers and service users. A manager can put both teams in a meeting, let them argue about the service definition, and then let them get back to work, confident that they know how they’ll communicate. A team consisting of Draco, Hermione, Luna and Harry, you could assign a service to each, put them in a room to define their service contracts (APIs), and manage progress based on the service contract. If Harry and Draco start arguing, you can pull out the service contract to resolve the problem and modify it if needed.
The SOA page on Wikipedia lists organizing principles behind services, but all boil down to dividing an application among developers, giving each one a clearly-defined service, and clear communication boundaries to increase confidence that the final project will work.
- Service contract
- Service discoverability
- Service loose-coupling
- Encapsulation
- Reusability
Resin 4.0 is organized around WebBeans, OSGi, and BAM to provide a foundation for application services. WebBeans is the cleanest technology so far for the service contract and discovery requirements. OSGi improves encapsulation and reusability by cleaning-up version management of libraries and services, using JVM classloader capabilities to improve the loose-coupling of services. BAM extends the foundation of WebBeans and OSGi to a distributed and clustered deployment, helping service architectures scale as demand grows.
- In 3.2, Resin’s web-apps are OSGi bundles, so you can define an OSGi service in the resin-web.xml.
- Resin’s OSGi/WebBeans integration lets you configure OSGi services in the resin-web.xml or web-beans.xml.
- The OSGi/WebBeans integration publishes OSGi services to WebBeans, letting service consumers inject directly.
Service example
The following code is an attempt to make the previous ideas about services concrete.
- service contract - Java interface in OSGi-exported package
- service implementation - Java code in OSGi-protected bundle
- service configuration - XML/WebBeans configuring the service for the consumer
- consumer implementation - WebBeans injection in consumer servlet
Service Contract (interface)
The service contract is a Java interface which is the boundary between the service provider and service user. If Draco is writing the service and Harry is using it, the Java interface is the contract the two rivals must agree on. From a management perspective, the contract organizes and limits the discussion, so the two can spend more time working, and less time arguing.
The Java package choice is important for OSGi because OSGi exports and hides based on package names. For OSGi encapsulation to work, the exported APIs need to be in a different package from the implementation. The package encapsulation lets clients program to a fixed contract (the exported package), while the service implementer can change the implementation as needed.
package foo;
public interface MyService {
public String hello();
}
Because WebBeans is usable when the service doesn’t use WebBeans at all, you can migrate an existing application to a cleaner WebBeans design piece by piece, and avoid a massive reorganization risk.
Service Implementation
As noted above, the implementation belongs in a separate package from the interface/contract, so OSGi can properly manage the exports and hide the implementation code. Since exported package names are seen by everyone, its best to give them priority in package names, i.e. the shorter names should be the export like “foo” and the longer, qualified names used by the implementation “foo.impl”.
Draco’s code is isolated in the foo.impl package and can be the bulk of his work.
package foo.impl;
import foo.MyService;
public class MyServiceImpl implements MyService {
public String hello()
{
return "hello, world";
}
}
Service Configuration
When using a service, you’ll often need to choose an implementation or configure parameters, like setting database parameters, or selecting an authentication server IP. For customization and configuration, a well-designed XML remains the best technology, and WebBeans provides the needed power without much distracting overhead.
In this example, I’ve configured the service as an OSGi-service, not just a WebBeans service, to show the minimal extra syntax. Exporting the service as an OSGi-service makes it available to other OSGi services, not just the web-app itself. Since this example only has the single service, it’s something of a stub.
<web-app xmlns="http://caucho.com/ns/resin"
xmlns:foo="urn:java:foo.impl">
<osgi-service>
<foo:MyServiceImpl/>
</osgi-service>
</web-app>
Service Use
Finally, Harry can write his consumer servlet, using WebBeans injection for the service discovery, and using the MyService API as his service contract with Draco. The consumer code is isolated from Draco’s implementation entirely. If necessary, Hermione can rewrite MyService in “bar.foo.impl” and Harry’s code will be isolated from the change.
package example;
import javax.servlet.*;
import javax.webbeans.*;
import foo.MyService;
public class MyServlet extends GenericServlet {
@javax.webbeans.Current MyService _service;
public void service(ServletRequest req, ServletResponse res)
{
PrintWriter out = res.getWriter();
out.println("hello: " + _service.hello());
}
}
