public portal of www.segersconsulting.com

Hello Spring DM server

Today I’m going to blog about writing and deploying a “Hello World” application on the new Spring DM server … in several bundles. Step by step I will show you how to create spring bundle projects and configure them into 1 web application. I based this blog on the webinar Building Web Applications for SpringSource Application Platform hosted by Sam Brannen.

According to the guys over at SpringSource:

SpringSource dm Serverâ„¢ is a completely modular, OSGi-based Java server designed to run enterprise Java applications and Spring-powered applications with a new degree of flexibility and reliability. The SpringSource dm Server is based on the new SpringSource Dynamic Module Kernelâ„¢ (dm Kernel). The dm Kernel provides a module-based backbone for the server, which also harnesses the power of Spring, Apache Tomcat and OSGi-based technologies.

What they are actually saying is that we can design our (Spring) web applications in such a way that we can split up our application in several OSGi bundles (or Spring Dynamic Moduls) and deploy them separately on their web server.

It sounds pretty fascinating, so let’s try it out …

What do we need to get started? Well, first of all you need the Spring DM server here. Secondly I dowloaded tried their Spring Source Tools Suite aswell here. The tool suite is not really a requirement but it get’s you started a little faster than tuning your Eclipse with the new server and Spring server plugins … and you get to try something new in the mean time.

Now we can start creating our Hello World app …

Step 1: Create the domain project

New Project in the Spring Source Tool Suite

New Project in the Spring Source Tool Suite

Create a new project, a SpringSource dm server Bundle project. I called it workshop-domain. In this project we are going to make our domain model. It’s not really rocket science to create a hello world domain model. Basically all we need is a HelloMessage class.

package hello.domain;

public class HelloMessage {

	String name;

	public HelloMessage() {
	}

	public HelloMessage(String name) {
		this.name = name;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

}

Our project will now look like this in our IDE:

Project View

Project View

Hurray!! We now have our domain model! But hold your horses, it’s not a bundle yet. When we created a bundle project we automagically created an OSGi manifest file. So we open our MANIFEST.MF file and we define our bundle like this … We basically expose the hello.domain

Manifest-Version: 1.0
Bundle-Name: Hello Domain
Bundle-ManifestVersion: 2
Bundle-SymbolicName: hello.domain
Bundle-Version: 1.0.0
Export-Package: hello.domain

Step 2: Create the service api

Next we create our service interface. This is even less than the domain model, but yet it’s another separate SpringSource dm server Bundle project. I called it workshop-service.api. In this project we have our HelloService interface.

package hello.service;

import hello.domain.HelloMessage;

public interface HelloService {

	String sayHello(HelloMessage helloMessage);

}

We also have our manifest file that we have to define like this:

Manifest-Version: 1.0
Bundle-Name: Hello Service Api
Bundle-ManifestVersion: 2
Bundle-SymbolicName: hello.service.api
Bundle-Version: 1.0.0
Import-Package: hello.domain
Export-Package: hello.service

We import the hello.domain and expose the hello.service in this bundle/project. Our project view should look like this:

Project View

Project View

Step 3: Create service implementation

We can now create another bundle project with our service implementation. We create separate bundles for our api and implementation just like we could create separate jars with the interfaces and implementations or put them in separate packages.

I called this project workshop-service.impl. In this project we create a spring service which we call HelloService. The service looks like this:

package hello.service.impl;

import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import hello.domain.HelloMessage;
import hello.service.HelloService;

@Service("helloService")
public class HelloServiceImpl implements HelloService {

	public String sayHello(HelloMessage helloMessage) {
		String name = helloMessage.getName();

		if (!StringUtils.hasText(name)){
			name = "World";
		}

		String response = "Hello " + name;
		return response;
	}

}

Notice that we have a spring service annotation @Service(“helloService”). We register this service in the module-context.xml. This file is the application context where we scan for our annotated spring beans. The service bean must also be exposed in the OSGi context so we also have an osgi-context.xml. Here we can see that we have a special OSGi namespace. We expose the helloservice bean here.

module-context.xml

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

     <context:component-scan base-package="hello.service.impl"/>

&lt;/beans&gt;</code></pre>
<p class="MsoNormal">osgi-context.xml</p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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/osgi
           http://www.springframework.org/schema/osgi/spring-osgi.xsd
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

     &lt;service ref="helloService" interface="hello.service.HelloService" /&gt;

&lt;/beans:beans&gt;

In the manifest file we import our other bundles, the domain and the api. We also import the spring library. (The Import-Library: is actually a little spring pimped OSGi but we’ll leave that discussion for someone else to blog).

Manifest-Version: 1.0
Bundle-Name: Hello Service Implementation - English
Bundle-ManifestVersion: 2
Bundle-SymbolicName: hello.service.impl_en
Bundle-Version: 1.0.0
Import-Package: hello.domain, hello.service
Import-Library: org.springframework.spring;version="[2.5.0,3.0.0)"

Step 4: Create the web app

Next we create our ‘web’ bundle, also a spring dm server bundle project, like all other projects for this application. I called the project workshop-web. In this project we have a spring mvc based controller called the HelloController.

package hello.web;

import hello.domain.HelloMessage;
import hello.service.HelloService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

	private final HelloService helloService;

	@Autowired
	public HelloController(HelloService helloService) {
		this.helloService = helloService;
	}

	@RequestMapping("/hello.html")
	public ModelAndView sayHello(@ModelAttribute HelloMessage msg){
		//msg currently not yet submitted
		String response = helloService.sayHello(msg);
		return new ModelAndView("hello", new ModelMap("response", response));
	}

}

The controller is nicely annotated with the Spring MVC annotations. (I was a little lazy and did not finish up the msg attribute which can be posted …)

The controller is initialized in the module-context.xml with the component-scan as is the helloservice. We configure a simple viewresolver aswell.

module-context.xml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;

    &lt;context:annotation-config/&gt;
    &lt;context:component-scan base-package="hello.web"/&gt;

	&lt;bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;
   		&lt;property name="prefix" value="/WEB-INF/jsp/"/&gt;
  		&lt;property name="suffix" value=".jsp"/&gt;
	&lt;/bean&gt;  

&lt;/beans&gt;

The helloService is referred to through the OSGi context.

osgi-context.xml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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/osgi
           http://www.springframework.org/schema/osgi/spring-osgi.xsd
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

     &lt;reference id="helloService" interface="hello.service.HelloService" /&gt;

&lt;/beans:beans&gt;

We also need our manifest file :

Manifest-Version: 1.0
Bundle-Name: Hello Web Module
Bundle-ManifestVersion: 2
Bundle-SymbolicName: hello.web
Bundle-Version: 1.0.0
Import-Package: hello.domain, hello.service,
 javax.servlet;version="[2.5.0,2.5.0]",
 javax.servlet.http;version="[2.5.0,2.5.0]"
Import-Library: org.springframework.spring; version="[2.5.0,3.0.0)"
Import-Bundle:
 com.springsource.org.apache.taglibs.standard; version="1.1.2",
 org.springframework.security;version="[2.0.0,2.1.0)"
Module-Type: Web
Web-ContextPath: hello_web
Web-DispatcherServletUrlPatterns: *.html

We can see the usual OSGi headers that define our bundle but we can also see some special Spring headers like Web-ContextPath and Web-DispatcherServletUrlPatterns. These headers are spring pimped OSGi headers and not really standard. They replace the need of of a web.xml configuration file. The spring OSGi headers are all explained in the programmers manual.

All that’s left now is our View part of the application, the jsps in our case. We create a WEB-INF directory under the MODULE-INF directory and put our jsp under there. We have a simple hello.jsp like this:

&lt;h2&gt;Workshop demo view (jsp)&lt;/h2&gt;
${response}

Step 5: Deploy the bundles

We can now deploy our bundles and see the magic unfold. Just drag the projects to the Spring DM server on the server view and fire it up. Then go to http://localhost:8080/hello_web/hello.html

The hello world page on the browser

The hello world page on the browser

Step 6: Hot swap modules

One of the most existing this of these OSGi modules on the spring DM server is the hot deploy/swap of a module. In our case we can swap the service implementation from English to Dutch (nl). See the screen shots ...

First the default setup: all our projects deployed

Server running with our bundles

Server running with our bundles

Second the alternative setup: we swapped the implementation bundle

Server running with alternative hello impl bundle

Server running with alternative hello impl bundle

The result after swapping the english implementation to the dutch one.

The result after swapping the english implementation to the dutch one.

Conclusion

The features offered in the server by the SpringSource team are definitely innovative and useful and didn't even show the PAR setup. OSGi is the future and they know it (just like they do at Sun in the aquarium which might be a decent competitor as a module based server). Although the features are cool there is a learning curve to set up these projects and understand the power of OSGi and Spring Modules. The Webinars help, and the examples are nice but something still seems to missing. Maybe it's IDE support like an uber wizard or scaffold code generation who knows ...

While trying the new server I user the Spring Tool Suite and although it's good, there were some problems with dependencies between the projects. I had to make the projects dependant on each other and restart my workspace before all depencies could be resolved ... strange but hey, I got it working and I'm sure that such problems will be fixed pretty soon.

And if you'r just scrolling down here for the source just click here. :)


Tagged as: , ,

You must be logged in to post a comment.