Saturday, May 29, 2010

JAX WS Web Services

If you cannot read this post properly, please go to the source at <a href="http://kolawarsunil.blogspot.com">http://kolawarsunil.blogspot.com</a>.

In this blog, we will see how easy it is to write a simple WebService and then add security to it.

First, write the Java Class with the methods that we are going to expose as a Web Service.

Example:


 

public class UserService{

public String getFullName(Long userId){

return "FULL NAME";

}

public String getSecretName(Long userId){

return "SECRET NAME";

}

}


 

Now, This simple java class methods can be exposed as a WebService, by adding few annotations(Java 6). After adding few annotations, the same class will look something like this.


 

@WebService(serviceName="UserWebService",name="UserServiceOperations",targetNamespace="http://ws.user.services.com/xsd/userservice")

public class UserService{

@WebMethod

public String getFullName((@WebParam(name = "userId") Long userId){

return "FULL NAME";

}


 

public String getSecretName(Long userId){

return "SECRET NAME";

}

}


 

As you can see in the above code, we add the @WebService annotation to expose this class as a web service, the attributes like serviceName, name, targetNamespace etc are optional. The serviceName need NOT be same as the Java Class name.

The @WebMehthod annotation exposes the method in the WebService. As one can see that 'getSecretName' method does not have the annotation, so it is not exposed as a WebService method.


 

The @WebParam is also not mandatory, but will be useful when the WSDL is generated from this class (we will look into generating WSDL in a moment) and the arguments the method takes are named as the value given in the @WebParam(name="something"). It would help the end user understand what that argument really is, instead of him knowing that it's a Long.


 

Once you write all the necessary annotations, we can go ahead and generate the WSDL from the Java Class (bottom up approach).

In maven, your pom.xml would look something like this (after adding the necessary dependencies and plug-ins to generate the WSDLs)


 


 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">


 

<parent>

<artifactId>parent_artifact</artifactId>

<groupId>parent_group</groupId>

<version>0.1</version>

</parent>


 

<modelVersion>4.0.0</modelVersion>

<groupId>group</groupId>

<artifactId>artifact</artifactId>

<version>0.2</version>

<packaging>war</packaging>

<name>User Service (WEB)</name>


 


 

<dependencies>

<dependency>

<groupId>org.jvnet.jax-ws-commons.spring</groupId>

<artifactId>jaxws-spring</artifactId>

<version>1.8</version>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>servlet-api</artifactId>

<version>2.5</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>2.5.6.SEC01</version>

<scope>test</scope>

<exclusions>

<exclusion>

<artifactId>junit</artifactId>

<groupId>junit</groupId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.4</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>2.5.6.SEC01</version>

</dependency>

<dependency>

<groupId>com.sun.xml.ws</groupId>

<artifactId>jaxws-rt</artifactId>

<version>2.1.3</version>

<exclusions>

<exclusion>

<artifactId>junit</artifactId>

<groupId>junit</groupId>

</exclusion>

</exclusions>

</dependency>

</dependencies>


 


 

<build>

<finalName>user_services_web</finalName>


 

<resources>

<resource>

<directory>src/main/resources</directory>

</resource>

</resources>

<testResources>

<testResource>

<directory>src/test/resources</directory>

</testResource>

</testResources>


 

<plugins>

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>jaxws-maven-plugin</artifactId>

<version>1.10</version>

<executions>


 

<execution>

<id>Generating User artifacts</id>

<goals>

<goal>wsgen</goal>

</goals>

<configuration>

<sei>com.services.ws.UserService</sei>

<genWsdl>true</genWsdl>

<keep>true</keep>

<resourceDestDir>src/main/webapp/WEB-INF/wsdl</resourceDestDir>

</configuration>

</execution>


 

<execution>

<id>Generating Consumer artifacts</id>

<goals>

<goal>wsgen</goal>

</goals>

<configuration>

<sei>com.services.ws.ConsumerWS</sei>

<genWsdl>true</genWsdl>

<keep>true</keep>

<resourceDestDir>src/main/webapp/WEB-INF/wsdl</resourceDestDir>

</configuration>

</execution>

</executions>


 

</plugin>


 

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-surefire-plugin</artifactId>

<configuration>

<skipTests>true</skipTests>

</configuration>

</plugin>


 

</plugins>

</build>


 

<repositories>

<repository>

<id>maven-repository.dev.java.net</id>

<name>Java.net Repository for Maven 1</name>

<url>http://download.java.net/maven/1/</url>

</repository>

<repository>

<id>maven2-repository.dev.java.net</id>

<name>Java.net Repository for Maven 2</name>

<url>http://download.java.net/maven/2/</url>

</repository>

</repositories>


 

<pluginRepositories>

<pluginRepository>

<id>maven2-repository.dev.java.net</id>

<url>http://download.java.net/maven/2/</url>

</pluginRepository>

</pluginRepositories>


 

</project>


 

As you can see in the above file, wsgen is used to generate the WSDL and XSD's. We need to specify the Service Endpoint Interface (SEI), in the tags <sei></sei>, where we specified the UserService class, with its fully qualified name (i.e along with the package etc)


 

You can right click on you pom.xml and select run as 'maven package', this will generate the WSDL's and keep them in the Directory location specified in the POM, in our case, we specified, <resourceDestDir>src/main/webapp/WEB-INF/wsdl</resourceDestDir>.


 

In case of any errors, open command prompt, go to the directory that contains the pom.xml and run the following command "mnv -X package". This will show the errors in detail (i assume you have mvn in your PATH, so that 'mvn' command can run from anywhere).


 

If you are using WebLogic 10.3, then @AutoWired (auto wiring in spring) does not work and we need to either inject the required bean in the context file or do the following


 

public class UserService{

@Resource

private WebServiceContext wsContext;


 

@Autowired

UserDAO userDao;


 

public UserDAO getUserDao() {


 

if (userDao== null) {

ServletContext servletContext = (ServletContext) wsContext.getMessageContext().get( MessageContext.SERVLET_CONTEXT);


 

WebApplicationContext webContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);

userDao= (UserDAO ) webContext.getBean("userDao");

}

return userDao;

}


 

}

Tuesday, May 11, 2010

Configuring HSQL for JUnit tests

Sometimes we need to test our code without being dependent on the Database. For example, if your project is being built continuously using cruise control, our JUnit tests cannot depend on the availability of Database connection. And also running so many tests connecting to DB takes lots of time.

To overcome this situation, we can use the HSQL in memory database and test against it.

Here is how we can configure using HSQL.

1) Download HSQL DB and put it in your classpath. OR if you are using Maven , you can add that dependency.

<dependency>

<groupId>hsqldb</groupId>

<artifactId>hsqldb</artifactId>

<version>1.8.0.1</version>

<scope>test</scope>

</dependency>




2) Change the properties in your text file as follows (We can also create another copy of the context file, which connects to HSQL DB)

<bean id="dataSource"

class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

<property name="driverClassName">

<value>org.hsqldb.jdbcDriver</value>

</property>

<property name="url">

<value>jdbc:hsqldb:mem:testdb</value>

</property>

<property name="username">

<value>sa</value>

</property>

<property name="password">

<value></value>

</property>



3) In the same file, add the following under 'hibernateProperties'. (This will create the tables in memory based on the already existing hbm files in your project)

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>

<prop key="hibernate.hbm2ddl.auto">create</prop> </props>

</property>



4) Now run the same JUnit test using the loading the new context file instead of old one. This time , the tables are automatically created in memory and the tests are run.