![]() | Draft |
|---|---|
This is a first draft of this chapter. It is technically accurate, but not in the style or state of the final revision. |
OSGi (Open Services Gateway interface) is a specification maintained by the OSGi Alliance. It was founded in March 1999. One of the goals of OSGi is to reduce complexity by having much better modularity and deployment of libraries and services. The increased modularity primarily comes from a much more fine grained classloading mechanism compared to a standard JVM or Java EE container. Libraries and packages needed at runtime must be explicitly defined and even the version or version ranges required can be specified. Also, what interfaces or classes your library exports are also clearly defined. A library (JAR archive) in OSGi terminology is referred to as a bundle.
There are a few different open source OSGi runtime environments. Some are Apache Felix, Knopflerfish, and Equinox. You may know Equinox as being what the Eclipse IDE is built on. Any Eclipse plugin is actually an OSGi bundle, so Eclipse has very good OSGi development support.
The Spring dm Server is also an OSGi server and is actually built on top of Equinox. Although many customizations and enhancements have been done to make it easier to manage and deploy bundles. Some of the enhancements are customizations to make it easier to deploy Spring bundles and also for deploying web bundles. In fact, standard WARs can be deployed as they are to any web container. Web modules can also be deployed as a thin WAR that uses OSGi imports to resolve any JARs instead of having them embedded in /WEB-INF/lib and also a more OSGi like web solution which will be shown in the next basic example.
SpringSource has made a very nice plugin for developing OSGi bundles and working with the Spring dm Server on the Eclipse IDE. The plugin provides project templates for a basic bundle and also a type called PAR. A PAR is a way to group together related bundles and deploy them together. The plugin also has Server support in the IDE that allows deploying projects to the server. As changes are made to your project, it will automatically keep deploying any changes to the bundle to the server. Also, when managing the project any OSGi bundle from the SpringSource can be downloaded directly into the Spring dm Server.
If you haven't already installed the IDE support for this and would like to run the examples, please refer to the Setup Appendix.
The Message Service example is comprised of a Message Service 1.0 bundle, Message Service 1.1 bundle, and also a very simple Web Module that displays the message from the service. The Message Service bundle, version 1.0, uses Commons Lang 2.1.0 and Message Service bundle, version 1.1, uses Commons Lang 2.4.0. This illustrates one of the features of OSGi, which is side by side versioning of libraries and being able to explicitly specify which version a bundle should use.
![]() | Build Process |
|---|---|
Currently the examples are setup to only run from the Eclipse environment and to be deployed using the Spring dm Server's IDE integration. A Maven build will eventually be added. |
The manifest defines the services name, description, it's symbolic name (will deployed under this name), version, and whatever libraries, bundles, and/or packages it imports. Also, whatever packages are exported are also defined.
![]() | Editing Manifest in Eclipse IDE |
|---|---|
If when opening the manifest and viewing the Dependencies tab, there isn't an 'Import Bundle' section on the right and an 'Import Library' section underneath it the default Eclipse manifest editor may have been opened. To open the Spring manifest editor, right click, 'Open with'/'Other...', and then choose the 'Bundle Manifest Editor'. |
Manifest-Version: 1.0
Bundle-Name: Simple Service Bundle
Bundle-Description: Simple Service
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.springbyexample.springindepth.sdms.message.messageService
Bundle-Version: 1.0.0
Import-Library: org.aspectj,
org.springframework.spring
Import-Bundle: com.springsource.org.apache.commons.lang;version="[2.1.0,2.1.0]"
Export-Package: org.springbyexample.springindepth.sdms.message.service;version="1.0.0"
By default any spring configuration files in the META-INF/spring will be processed (this is configurable). Following the naming conventions setup by Spring Dynamic Modules for OSGi(tm) Service Platforms there are two Spring configuration files. One is called bundle-context.xml and the other is called bundle-context-osgi.xml. The first one is for standard Spring configuration and the latter is meant for Spring Dynamic Modules specific configuration. Like exposing OSGi services or defining services as bean references.
The MessageServiceImpl is defined as a messageService bean which will
be exposed as a service in the bundle-context-osgi.xml configuration file.
Spring's context:component-scan could have also been used, but wasn't just to keep the
example a little more clear (see more about context:component-scan in
Annotation-based Bean Creation).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageService"
class="org.springbyexample.springindepth.sdms.message.service.impl.MessageServiceImpl"
p:message="Greetings!"/>
</beans>
The Spring Dynamic Modules' namespace is
setup as the default namespace for the configuration file. The service
element exposes the messageService bean as an OSGi service under the
interface org.springbyexample.springindepth.sdms.message.service.MessageService.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/osgi"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<service ref="messageService" interface="org.springbyexample.springindepth.sdms.message.service.MessageService"/>
</beans:beans>
The code consists of the MessageService interface and the implementation MessageServiceImpl.
It's just an interface to get a message and the implementation uses Commons Lang
Example 8.1. Message Service MessageService (version 1.0)
public interface MessageService {
/**
* Gets message.
*/
public String getMessage();
}
This implementation uses FastDateFormat from Commons Lang 2.1.0 to display a formatted date
after the message that will be injected from the Spring configuration.
Example 8.2. Message Service MessageServiceImpl (version 1.0)
public class MessageServiceImpl implements MessageService {
private String message = null;
/**
* Gets message.
*/
public String getMessage() {
StringBuffer sb = new StringBuffer();
sb.append(message);
sb.append(" [");
sb.append(FastDateFormat.getInstance("MM/dd/yy").format(new Date()));
sb.append("]");
return sb.toString();
}
/**
* Sets message.
*/
public void setMessage(String message) {
this.message = message;
}
}
This is the same as the 1.0 version of the Message Service bundle, but it has it's version set to 1.1.0 on the Bundle-Version property and also depends on Commons Lang 2.4.0.
Manifest-Version: 1.0
Bundle-Name: Simple Service Bundle
Bundle-Description: Simple Service
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.springbyexample.springindepth.sdms.message.messageService
Bundle-Version: 1.1.0
Import-Library: org.aspectj,
org.springframework.spring
Import-Bundle: com.springsource.org.apache.commons.lang;version="[2.4.0,2.4.0]"
Export-Package: org.springbyexample.springindepth.sdms.message.service;version="1.1.0"
The version 1.1 of MessageServiceImpl has a different message than version 1.0.
Also the implementation uses a Commons Lang 2.4.0 specific method call that is appended to the message.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageService"
class="org.springbyexample.springindepth.sdms.message.service.impl.MessageServiceImpl"
p:message="Greetings from 'Spring In-depth, In Context'!"/>
</beans>
The messageService bean is exposed as a service under the
interface org.springbyexample.springindepth.sdms.message.service.MessageService.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/osgi"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<service ref="messageService" interface="org.springbyexample.springindepth.sdms.message.service.MessageService"/>
</beans:beans>
The MessageService interface is identical to the one in the Message Service Bundle 1.0 class.
So the Message Service Web Module can dynamically use either implementation's service.
Example 8.3. Message Service MessageService (version 1.1)
public interface MessageService {
/**
* Gets message.
*/
public String getMessage();
}
This implementation uses FastDateFormat from Commons Lang 2.4.0 to display a formatted date
after the message that will be injected from the Spring configuration. It also uses StringUtils.length(String)
to add the message length at the end of the message. This method was adding Commons Lang 2.4.0. If you try to use this
method in the Message Service Bundle 1.0 implementation, you will see that it won't be able to find that method on StringUtils.
Example 8.4. Message Service MessageServiceImpl (version 1.1)
public class MessageServiceImpl implements MessageService {
private String message = null;
/**
* Gets message.
*/
public String getMessage() {
StringBuffer sb = new StringBuffer();
sb.append(message);
sb.append(" [");
sb.append(FastDateFormat.getInstance("MM/dd/yyyy").format(new Date()));
sb.append("]");
// add Commons Lang StringUtils.length(String) which was specifically added in Commons Lang 2.4.0)
sb.append(" (message length=");
sb.append(StringUtils.length(message));
sb.append(")");
return sb.toString();
}
/**
* Sets message.
*/
public void setMessage(String message) {
this.message = message;
}
}
The web module's manifest is very similar to the other bundles, but it imports the taglib and servlet bundles. Also instead of exporting a package, it imports the Message Service package exported by the Message Service bundles and specifies that is can use version 1.0.0 through 1.1.0.
Manifest-Version: 1.0
Bundle-Name: Simple Service Web Module
Bundle-SymbolicName: org.springbyexample.springindepth.sdms.message.webModule
Bundle-Version: 1.0.0
Bundle-Vendor: Spring In-depth, In Context
Bundle-ManifestVersion: 2
Import-Bundle: com.springsource.org.apache.taglibs.standard,
com.springsource.javax.servlet
Import-Library: org.aspectj;version="[1.6.0,1.7.0)",
org.springframework.spring;version="[2.5.5,3.0.0)"
Module-Type: Web
Web-ContextPath: message
Web-DispatcherServletUrlPatterns: *.html
Import-Package: org.springbyexample.springindepth.sdms.message.service;version="[1.0.0,1.1.0]"
The bundle-context.xml isn't used in this example, but bundle-context-osgi.xml makes a reference to the Message Bundles Service and exposes it as a bean. The Spring web configuration is in webmvc-context.xml.
The reference to the OSGi service MessageService is exposed as a bean
named messageSerice.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/osgi"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<reference id="messageService" interface="org.springbyexample.springindepth.sdms.message.service.MessageService"/>
</beans:beans>
This is just a standard Spring MVC configuration and the controller is register using context:component-scan.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.springbyexample.springindepth.sdms.web.message.mvc" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors" ref="localeChangeInterceptor"/>
</bean>
<!-- Enables annotated POJO @Controllers -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- Enables plain Controllers -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<value>messages</value>
</property>
</bean>
<!-- Declare the Interceptor -->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="locale"/>
</bean>
<!-- Declare the Resolver -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
</beans>
This isn't the ideal way to do things, but to reduce the complexity of the example the controller just puts the message into request scope and is rendered on the page using JSTL.
<html>
<head>
<title>Message</title>
</head>
<body>
<h1>Message</h1>
<p>${message}</p>
</body>
</html>
The controller has the message service defined in bundle-context-osgi.xml
injected using @Autowired. The display method maps to
/display/index.html" and puts the results of the message service
into request scope.
Example 8.5. Mesage Service MessageController
@Controller
public class MessageController {
@Autowired
protected MessageService messageService = null;
/**
* Displays RMI info.
*/
@RequestMapping(value="/display/index.html")
public void display(HttpServletRequest request) {
request.setAttribute("message", messageService.getMessage());
}
}