About

This is the default template for Pivot. You can change this text by editing the file frontpage_template.html in your pivot/templates/ folder. You can do this by directly editing the file, or you can go to Administration » Templates in the Pivot interface.

Tag cloud

(all)

Archives

01 Feb - 28 Feb 2009
01 Apr - 30 Apr 2009
01 Jun - 30 Jun 2009
01 Jul - 31 Jul 2009
01 Aug - 31 Aug 2009
01 Oct - 31 Oct 2009
01 Nov - 30 Nov 2009
01 Jan - 31 Jan 2010
01 Mar - 31 Mar 2010
01 Jul - 31 Jul 2010
01 Oct - 31 Oct 2010
01 Nov - 30 Nov 2010
01 Jun - 30 Jun 2011
01 Nov - 30 Nov 2011

Links

Dawn

Search!

Last Comments

Polo Outlet (Slides for the Da…): Very good, very good. Let…
Polo Outlet (It's demo time): Very good, very good. Let…
Coach Outlet Stor… (Slides for the Da…): why it really is viewed a…
Coach Outlet Stor… (It's demo time): why it really is viewed a…
Burberry Outlet O… (Slides for the Da…): Hi, this is my first visi…
Burberry Outlet O… (It's demo time): Hi, this is my first visi…
Burberry Outlet O… (Distributed Ecore…): Hi, this is my first visi…
Coach Outlet Stor… (Distributed Ecore…): why it really is viewed a…
Polo Outlet (Distributed Ecore…): Very good, very good. Let…
qifei2012 (Slides for the Da…): labeled colours significa…

Stuff

Powered by Pivot - 1.40.6: 'Dreadwind' 
XML: RSS Feed 
XML: Atom Feed 

Equinox, Jetty and Jax-WS

Thursday 30 December 2010 at 3:40 pm Developing web apps with J2EE is quite easy. But handling all those dependencies in the WEB-INF folder can be a real pain. Especially if you are used to OSGi and quite modular applications. Well, luckily someone came to the nice idea to bring both things together which finally resulted in OSGI RFC 66. This implementation on Equinox together with Jetty brings up a nice modular development environment for web application. But thinks could become more complicated if SOAP-based web services are involved. Recently I tried to bring up some services on a Jetty/Equinox based application which turned out to be more complicated than expected.

When I finally had all pieces together it did not look that complicated, but the road was a quite rocky one. Since I did not find a tutorial which described how to bring up Jax-WS services on a Jetty/Equinox platform I decided to write my own...and here it is ;)

First, download a fresh Eclipse from here. We will use a fresh IDE to show which additional dependencies are needed if you start from the scratch.

After downloading, unzipping and starting the IDE we will add those dependencies to your development environment.
First you need to install the correct runtime dependencies for Jetty. We are using Jetty 7.1.3 which can be retrieved from the Jetty update page http://download.eclipse.org/jetty/7.1.3.v20100526/repository).
From the update site select "Jetty Target Components" and install this feature.




Now we will focus on the business logic. We will take a small, well, very small example for our webservice. Just imagine that you would have a toll which allows collecting and manipulating data about people. First we will create a small bundle which will act as a container for our business data. Select File->New->Other and create a Plug-ing Project names "org.mftech.examples.web.persons"






We won't make any UI contribution and will not create an RCP.



Next we need a class to hold the information for a person. Let's be a bit creative and call it...Person. A Person should be named and we want to know how old a person is. In addition we want to collect the same information for the person's children. And it might be interesting which sex a person has. We will also provide a unique identifier to make it easier to find a person in our system. Putting all this together results in the following class.





package org.mftech.examples.web.persons;

import java.util.ArrayList;
import java.util.List;

public class Person
{
public enum Sex
{
male, female, extraterrestrial_hybrid
}

private int id;

private Sex sex;

private String name;

private int age;

private List<Person> children;

public void setSex(Sex sex)
{
this.sex = sex;
}

public Sex getSex()
{
return sex;
}

public void setName(String name)
{
this.name = name;
}

public String getName()
{
return name;
}

public void setAge(int age)
{
this.age = age;
}

public int getAge()
{
return age;
}

public void setChildren(List<Person> children)
{
this.children = children;
}

public List<Person> getChildren()
{
if (children == null)
{
children = new ArrayList<Person>();
}

return children;
}

public void setId(int id)
{
this.id = id;
}

public int getId()
{
return id;
}
}



I promised to keep it simple so that's enough for our business model. Now let us focus on the web site of the story. To handle out web container will again will create a bundle. This time named "org.mftech.exampples.web.soap". We will create a pure OSGi bundle.



Since we want to use the cool RFC 66 features the first thing we need to do is to tweak the Manifest.MF a bit. Add the Web-ContextPath to the file and make it look like the one below.



Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Soap
Bundle-SymbolicName: org.mftech.exampples.web.soap
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: org.mftech.exampples.web.soap.Activator
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0"
Web-ContextPath: /persons



Because the RFC66 implementation will search the bundle for a WEB-INF folder containing a web.xml we need to provided both. Create the folder and place in the web.xml.




<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<display-name>Hello Webapp</display-name>

<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>



This currently does nothing but to define the welcome files. But I know that you're are keen on seeing the server running, so let's create a simple index.html.




<html>
<head></head>
<body>
<b>Server is up and running.<b>
</body>
</html>



Now go to the runtime configuration, create a new "OSGi Framework" configuration and and select all Jetty 7.1.3 bundles. Do not forget to hit the "Add Required Bundles" button.



After starting the configuration change to the following URL <a href="http://localhost:8080/persons/" title="">http://localhost:8080/persons/</a>
This will show the content of our small index.html



So you see, it's quite easy to bring up a moduolar web application. But this is just the beginning. Now let'S focus on the web service. Our service should allow to create, manipulate and delete people from a list. To have a clean design we will form a nice interface with these requirements.




package org.mftech.exampples.web.soap.registry;
import java.util.List;
import org.mftech.examples.web.persons.Person;
public interface IPersonRegistry
{
public int addPerson(Person person);
public void deletePerson(Person person);
public List<Person> getAllPersons();
public void changeValues(int id, Person person);
}



This small interface make more or less sense. We have a method to add a Person which returns the new id assigned to the person. We can also delete a Person, which will be identified by it's id. In addition we can retrieve a list of all registered Persons and finally we can overwrite the values of a person with another Person object. You'll notice that the Person class could not be resolved. To make it available in our soap bundle we must export it form it's own bundle and add the specific dependencies to the bundle that contains out interface.




Now it is time to care for the implementation. I'll make it quick and simply provide the implementation to you. Actually the data is stored in a hash map and we do not care for validation to keep it simple.




package org.mftech.exampples.web.soap.registry.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jws.WebService;

import org.mftech.examples.web.persons.Person;
import org.mftech.exampples.web.soap.registry.IPersonRegistry;

@WebService
public class PersonRegistry implements IPersonRegistry
{
public static IPersonRegistry instance= new PersonRegistry();

public static Map<Integer, Person> persons=new HashMap<Integer, Person>();

@Override
public int addPerson(Person person)
{
int index=persons.size();
person.setId(index);
persons.put(index,person);
return index;
}

@Override
public void deletePerson(Person person)
{
persons.remove(person.getId());
}

@Override
public List<Person> getAllPersons()
{
return new ArrayList<Person>(persons.values());
}

@Override
public void changeValues(int id, Person person)
{
person.setId(id);
persons.put(id, person);
}
}



The only important thing is that the class is annotated with the @Webservice annotation. This makes the class as a web service for the JAX-WS implementation shipped with your JRE. At this point we could start the web service using the Endpoint API. But this is the least elegant way so we will do it slightly different. We will deploy our service declarative. To do this we must a file called "sun-jaxws.xml" to out WEB-INF folder. This file describes the parameters of our service and allows us to deploy it together with all other resources.




<?xml version="1.0" encoding="UTF-8"?>

<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="PersonRegistry"
implementation="org.mftech.exampples.web.soap.registry.impl.PersonRegistry"
url-pattern="/PersonRegistry/service" />
</endpoints>



Additionally we need to update our web.xml to provide a listener for webservice calls.




...
<listener>
<listener-class>
com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>

<servlet>
<servlet-name>WSServlet</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>WSServlet</servlet-name>
<url-pattern>/PersonRegistry/service</url-pattern>
</servlet-mapping>
...



The contextListener is not yet know to our application so we need to download the JAX-WS dependencies (http://jax-ws.java.net/). Since I had some trouble with version 2.2.1 and die WS action stuff I recommend to use version 2.1 which can be downloaded from here. Just unpack the stuff and copy the lib folder under your WEB-INF folder.

Unfortunately, there is one last thing to do. We need to generate wrapper classes for JAX-WS. This can be done with the wsgen tool from your JRE. Because I do not want to bother you with the details of this tool, I wrote a small ant file which compiles the classes for you. Just copy the build_server.xml to the root of your "org.mftech.exampples.web.soap" bundle and execute it as ant script. You might need to change to path to your JRE.




<?xml version="1.0"?>
<project name="My Project" default="start">
<property name="jdk.home" value="C:/Program Files/Java/jdk1.6.0_18" />
<property name="gen.serviceclass.name" value="PersonRegistry" />
<property name="gen.serviceclass"
value="org.mftech.exampples.web.soap.registry.impl.${gen.serviceclass.name}" />
<property name="gen.server" value="wsgen.exe" />
<property name="gen.project.root" value='${basedir}' />
<property name="gen.server.cp"
value='.;${gen.project.root}/bin;../org.mftech.examples.web.persons/bin;' />

<target name="start">
<echo>${dir}</echo>
<echo> ${java.home}</echo>
<echo> ${jdk.home}</echo>
<antcall target="server" />

</target>

<target name="server">

<property name="gen.server.command" value='"${jdk.home}/bin/${gen.server}" -cp "${gen.server.cp}"
${gen.serviceclass} -s "${gen.project.root}/src-gen"' />

<echo>${gen.server.command}</echo>
<exec executable="${gen.server.command}">
</exec>

</target>
</project>




Now it is time to restart out web bundle and see the fruits of our work. Just open the following URL in your browser http://localhost:8080/persons/PersonRegistry/service . You'll see a page which lists all registered services.



Before we start writing our client we will create a small Servlet that helps us to keep track of the changes in our person registry. Therefore we must add "javax.servlet" and "javax.servlet.http" to our imported packages.

<!--


package org.mftech.exampples.web.soap.view;

import java.io.IOException;
import java.io.Writer;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mftech.examples.web.persons.Person;
import org.mftech.exampples.web.soap.registry.impl.PersonRegistry;

public class ShowPersonsServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
Writer writer = resp.getWriter();

writer.write("<head><meta http-equiv='refresh' content='5; URL=/persons/showPersons'></head>");
writer.write("<h2>PersonRegistry</h2>");

List<Person> allPersons = PersonRegistry.instance.getAllPersons();
writer.write("<p>" + allPersons.size() + " persons are registred.</p>");

writer.write("
writer.write("<tr>");
writer.write("<td>Id</td><td>Name</td><td>Age</td><td>No. children</td>");
for (Person person : allPersons)
{
writer.write("<tr>");
writer.write("<td>" + person.getId() + "</td>");
writer.write("<td>" + person.getName() +"</td>");
writer.write("<td>" + person.getAge() + "</td>");
writer.write("<td>" + person.getChildren().size() + "</td>");

writer.write("</tr>");
}
writer.write("
");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
super.doGet(req, resp);
}
}

-->

O.k. I admit that this is not the most sophisticated high end design, but this is just an examples. Now let us register it in the web.xml




...
<servlet>
<servlet-name>registry</servlet-name>
<servlet-class>org.mftech.exampples.web.soap.view.ShowPersonsServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>registry</servlet-name>
<url-pattern>/showPersons</url-pattern>
</servlet-mapping>
...



If we then restart our server and check this URL http://localhost:8080/persons/showPersons we will see that no person is currently registered.



Now, finally we will write the client. Create another plugin called "org.mftech.exampples.web.soap.client". To generate the client you could use the WTP tool to generate an Axis or Apache CXF client, but we will stay with the default JAX-WS implementation and use the tool wsimport. Again I made the generation a bit easier for you by providing a small a script which must be executed from the root of your client plugin.




<?xml version="1.0"?>
<project name="My Project" default="start">
<property name="jdk.home" value="C:/Program Files/Java/jdk1.6.0_18" />
<property name="gen.client.project.root" value='${basedir}\..\org.mftech.exampples.web.soap.client' />
<property name="gen.endpoint" value="http://localhost:8080/persons/PersonRegistry/service" />

<path id="jaxws.classpath">
<fileset dir="${basedir}\..\org.mftech.exampples.web.soap\WEB-INF\lib">
<include name="**/*.jar" />
</fileset>
</path>
<taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
<classpath refid="jaxws.classpath" />
</taskdef>

<target name="start">
<echo>${dir}</echo>
<echo> ${java.home}</echo>
<echo> ${jdk.home}</echo>
<antcall target="client" />
</target>

<target name="client">
<wsimport wsdl="${gen.endpoint}?WSDL" sourcedestdir="${gen.client.project.root}\src-gen"
package="org.mftech.exampples.web.soap.client"
verbose="true" debug="true" xendorsed="true" />
</target>

</project>



After this is done the only thing left is to write a small test client which manipulates the data.




package org.mftech.exampples.web.soap.client;

import java.util.List;
import java.util.Random;

public class WebServiceTester
{
public static void main(String[] args) throws InterruptedException
{
int c = 65;
int loopCount = 0;
while (true)
{
PersonRegistry registry = new PersonRegistryService().getPersonRegistryPort();
List<Person> allPersons = registry.getAllPersons();

if (loopCount % 4 != 0)
{
Person person = new Person();
person.setName("Person " + (char) (c++));
person.setAge(new Random().nextInt(80)+20);

int id = registry.addPerson(person);

System.out.println("Added Person with id: " + id);
}
else
{
if (allPersons.size() > 0)
{
registry.deletePerson(allPersons.get(0));
}
}

loopCount++;
Thread.sleep(5000);
}

}
}



Now start the server and then the client and enjoy the show. ;)

Linkdump