Showing posts with label JAX-WS. Show all posts
Showing posts with label JAX-WS. Show all posts

Sunday, December 4, 2011

Solution for JDK6 + JBoss-5.1.0GA + JAX-WS integration error: java.lang.UnsupportedOperationException: setProperty must be overridden by all subclasses of SOAPMessage

1.     Copy the following jars from JBOSS_HOME/client to JBOSS_HOME/lib/endorsed dir.
        a.     jbossws-native-jaxrpc.jar
        b.     jbossws-native-jaxws.jar
        c.     jbossws-native-jaxws-ext.jar
        d.     jbossws-native-saaj.jar

2.     Delete saaj-impl.jar(if its already there) from JBOSS_HOME/lib/endorsed dir.

Monday, October 3, 2011

Developing WebServices using JDK6/JAX-WS is simple. Is it true?

In many articles, blogs we can see how to develop WebServices using with JDK6's JAX-WS in-built support in just 5 minutes.

We can simply write a POJO and annotate it with @WebService, publish it with Endpoint.publish(...) and you can see the generated wsdl by pointing your browser to http://localhost:8080/JAXWS/helloService?wsdl.

Immediately we can write a client and call helloPort.sayHello("siva") and you will get "Hello Siva!!!" response from your HelloWebService.

In the first look it feels like developing web services is very simple using JDK6's in-built JAX-WS support.

With that confidence immediately I thought of writing a bit more complex WebService and deploy it on Tomcat.

As we can find number of articles on how to develop/deploy JAX-WS webservices on Tomcat Server, I started writing my next ABitComplexService and deployed on Tomcat and saw the generated WSDL file.

Then I generated client stubs using wsimport and invoked webservice methods and it worked fine... :-)

Then I though of invoking the webservice from a simple web project and put the client code in a new Web web project and and call the web service.

Now the show begins :-)

Then I got an opportunity to know what is JDK's endorsed directory, how App Servers have their specific endorsed directories etc etc.

I got weird errors like:

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: javax.xml.ws.WebFault.messageName()Ljava/lang/String;

java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.bind.api.JAXBRIContext

After struggling for sometime I copied jaxws-api.jar/jaxb-api.jar to endorsed directories and finally get it worked.


Now I wanted to make it a bit more complicated and thought of creating another WebService and deploy on JBoss Server and call it from the first WebService deployed on Tomcat.

Now I have:

1. MySecondWS webservice deployed on JBoss.
2. MyFirstWS webservice deployed on Tomcat. It is a Service Provider and Consumer(for MySecondWS) as well.
3. MyWebClient a Dynamic Web Project. Its a client for MyFirstWS.

Here as MyFirstWS webservice is a Service provider I have all the JAX-WS RI jars in WEB-INF/lib directory.

All set and I deployed all the Apps on their servers and hit the Submit button triggering MyFirstWS webservice call().

I got weird errors like NullPointerExceptions, MetadataModelExceptions etc.

After some trials I have identified the pattern that if I invoke the client from a project which has JAXWS-RI jars in its classpath I am getting this error.

But I need to develop a WebService which is both Service Provider and Consumer...
For the Service Producer I need to have those Jars in my classpath and at the same time to be Service Consumer I shouldn't have those jars in my WEB-INF/lib.

So what to do? Yes, I know I need some more time to Google and I can find a way how to do this as this is very common requirement. But I feel like Developing Web Services using JDK6's JAX-WS in-build support is not as easy as Advertised(My opinion only).

Anyway this weekend I understood two things:
1. Developing (Real)WebServices with JAX-WS is not that simple as advertised in the Articles/Blogs.
2. You can easily waste your week-ends by entering into Java-Jar-Hell :-)

Friday, September 30, 2011

Deploying JAX-WS WebService on Tomcat-6

Now we are going to see how to deploy JAX-WS WebService on Tomcat Server.
We are going to deploy The AuthenticationService developed in http://sivalabs.blogspot.com/2011/09/developing-webservices-using-jax-ws.html on apache-tomcat-6.0.32.

To deploy our AuthenticationService we need to add the following configuration.

1.web.xml

<web-app>
 <listener>
  <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
 </listener>
 
 <servlet>
  <servlet-name>authenticationService</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>authenticationService</servlet-name>
  <url-pattern>/services/AuthenticationService</url-pattern>
 </servlet-mapping>
</web-app> 

2. Create a new file WEB-INF/sun-jax-ws.xml

<?xml version="1.0" encoding="UTF-8"?>
<endpoints
  xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
  version="2.0">
  
  <endpoint
      name="AuthenticationService"
      implementation="com.sivalabs.caas.services.AuthenticationServiceImpl"
      url-pattern="/services/AuthenticationService"/>
      
</endpoints>

3. Download the JAX-WS Reference Implementation from http://jax-ws.java.net/
Copy all the jar files from jaxws-ri/lib folder to WEB-INF/lib.


Now deploy the application on Tomcat server.
You don't need to publish the Service by our-self as we did using EndpointPublisher.
Once the tomcat is up and running see the generated wsdl at http://localhost:8080/CAAS/services/AuthenticationService?wsdl.

Now if you test the AuthenticationService using standalone client it will work fine.
public static void testAuthenticationService()throws Exception
{
  URL wsdlUrl = new URL("http://localhost:8080/CAAS/services/AuthenticationService?wsdl");
  QName qName = new QName("http://sivalabs.blogspot.com/services/AuthenticationService", "AuthenticationService");
  Service service = Service.create(wsdlUrl,qName);
  AuthenticationService port = service.getPort(AuthenticationService.class);
  Credentials credentials=new Credentials();
  credentials.setUserName("admin");
  credentials.setPassword("admin");
  AuthenticationStatus authenticationStatus = port.authenticate(credentials);
  System.out.println(authenticationStatus.getStatusMessage());
}

But if you try to test with the wsimport tool generated client code make sure that you dont have jax-ws-ri jars in Client classpath.
Otherwise you will get the below error:

Exception in thread "main" java.lang.NoSuchMethodError: javax.xml.ws.WebFault.messageName()Ljava/lang/String;
 at com.sun.xml.ws.model.RuntimeModeler.processExceptions(RuntimeModeler.java:1162)
 at com.sun.xml.ws.model.RuntimeModeler.processDocWrappedMethod(RuntimeModeler.java:898)

Thursday, September 29, 2011

Developing WebServices using JAX-WS

Let us assume an enterprise is maintaining user authentication details in a centralized system. We need to create an AuthenticationService which will take credentials, validate them and return the status. The rest of the applications will use the AuthenticationService to authenticate the Users.

Create AuthenticationService interface as follows:

package com.sivalabs.caas.services;

import javax.jws.WebService;

import com.sivalabs.caas.domain.AuthenticationStatus;
import com.sivalabs.caas.domain.Credentials;
import com.sivalabs.caas.exceptions.AuthenticationServiceException;

@WebService
public interface AuthenticationService
{
public AuthenticationStatus authenticate(Credentials credentials) throws AuthenticationServiceException;
}



package com.sivalabs.caas.domain;

/**
 * @author siva
 *
 */
public class Credentials 
{
 private String userName;
 private String password;
 public Credentials() 
{
 }
 public Credentials(String userName, String password) {
  super();
  this.userName = userName;
  this.password = password;
 }
 //setters and getters
 
}


package com.sivalabs.caas.domain;

/**
 * @author siva
 *
 */
public class AuthenticationStatus
{
 private String statusMessage;
 private boolean success;
 //setters and getters
 
}


package com.sivalabs.caas.exceptions;

/**
 * @author siva
 *
 */
public class AuthenticationServiceException extends RuntimeException
{
 
 private static final long serialVersionUID = 1L;
 public AuthenticationServiceException()
 {
 }
 public AuthenticationServiceException(String msg)
 {
  super(msg);
 }
}



Now let us implement the AuthenticationService.

package com.sivalabs.caas.services;

import java.util.HashMap;
import java.util.Map;

import javax.jws.WebService;

import com.sivalabs.caas.domain.AuthenticationStatus;
import com.sivalabs.caas.domain.Credentials;
import com.sivalabs.caas.exceptions.AuthenticationServiceException;

/**
 * @author siva
 *
 */
@WebService(endpointInterface="com.sivalabs.caas.services.AuthenticationService",
   serviceName="AuthenticationService", 
   targetNamespace="http://sivalabs.blogspot.com/services/AuthenticationService")
public class AuthenticationServiceImpl implements AuthenticationService
{
 private static final Map<string, string> CREDENTIALS = new HashMap<string, string>();
 static
 {
  CREDENTIALS.put("admin", "admin");
  CREDENTIALS.put("test", "test");  
 }
 
 @Override
 public AuthenticationStatus authenticate(Credentials credentials) throws AuthenticationServiceException
 {
  if(credentials == null)
  {
   throw new AuthenticationServiceException("Credentials is null");
  }
  AuthenticationStatus authenticationStatus = new AuthenticationStatus();
  String userName = credentials.getUserName();
  String password = credentials.getPassword();
  
  if(userName==null || userName.trim().length()==0 || password==null || password.trim().length()==0)
  {
   authenticationStatus.setStatusMessage("UserName and Password should not be blank");
   authenticationStatus.setSuccess(false);
  }
  else
  {
   if(CREDENTIALS.containsKey(userName) && password.equals(CREDENTIALS.get(userName)))
   {
    authenticationStatus.setStatusMessage("Valid UserName and Password");
    authenticationStatus.setSuccess(true);
   }
   else
   {
    authenticationStatus.setStatusMessage("Invalid UserName and Password");
    authenticationStatus.setSuccess(false);
   }
  }
  return authenticationStatus;
 }
}

Here for simplicity we are checking the credentials against the static data stored in HashMap. In real applications this check will be done against database.

Now we are going to publish the WebService.

package com.sivalabs.caas.publisher;

import javax.xml.ws.Endpoint;

import com.sivalabs.caas.services.AuthenticationServiceImpl;

public class EndpointPublisher
{
 public static void main(String[] args)
 {
  Endpoint.publish("http://localhost:8080/CAAS/services/AuthenticationService", new AuthenticationServiceImpl());
 }

}
Run this standalone class to publish the AuthenticationService.

To check whether the Service published successfully point the browser to URL http://localhost:8080/CAAS/services/AuthenticationService?wsdl. If the service published successfully you will see the WSDL content.


Now let us create a Standalone test client to test the webservice.

package com.sivalabs.caas.client;

import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;

import com.sivalabs.caas.domain.AuthenticationStatus;
import com.sivalabs.caas.domain.Credentials;
import com.sivalabs.caas.services.AuthenticationService;

/**
 * @author siva
 *
 */
public class StandaloneClient
{

 public static void main(String[] args) throws Exception
 {
  URL wsdlUrl = new URL("http://localhost:8080/CAAS/services/AuthenticationService?wsdl");
  QName qName = new QName("http://sivalabs.blogspot.com/services/AuthenticationService", "AuthenticationService");
  Service service = Service.create(wsdlUrl,qName);
  AuthenticationService port = service.getPort(AuthenticationService.class);
  Credentials credentials=new Credentials();
  credentials.setUserName("admin1");
  credentials.setPassword("admin");
  AuthenticationStatus authenticationStatus = port.authenticate(credentials);
  System.out.println(authenticationStatus.getStatusMessage());
  
  credentials.setUserName("admin");
  credentials.setPassword("admin");
  authenticationStatus = port.authenticate(credentials);
  System.out.println(authenticationStatus.getStatusMessage());
 }

}
Instead of writing StandaloneClient by our-self we can generate the Client using wsimport commandline tool.
wsimport tool is there in JDK/bin directory.
Go to your project src directory and execute the following command.
wsimport -keep -p com.sivalabs.caas.client http://localhost:8080/CAAS/services/AuthenticationService?wsdl

It will generate the following java and class files in com.sivalabs.caas.client package.

Authenticate.java
AuthenticateResponse.java
AuthenticationService_Service.java
AuthenticationService.java
AuthenticationServiceException_Exception.java
AuthenticationServiceException.java
AuthenticationStatus.java
Credentials.java
ObjectFactory.java
package-info.java

Now you can use the generated Java files to test the Service.

public static void main(String[] args) throws Exception
{
  AuthenticationService_Service service = new AuthenticationService_Service();
  com.sivalabs.caas.client.AuthenticationService authenticationServiceImplPort = service.getAuthenticationServiceImplPort();
  com.sivalabs.caas.client.Credentials credentials = new com.sivalabs.caas.client.Credentials();
  
  credentials.setUserName("admin1");
  credentials.setPassword("admin");
  com.sivalabs.caas.client.AuthenticationStatus authenticationStatus = authenticationServiceImplPort.authenticate(credentials);
  System.out.println(authenticationStatus.getStatusMessage());
  
  credentials.setUserName("admin");
  credentials.setPassword("admin");
  authenticationStatus = authenticationServiceImplPort.authenticate(credentials);
  System.out.println(authenticationStatus.getStatusMessage());
}

In next article I will show how to deploy this AuthenticationService in Tomcat.