Saturday, March 5, 2011

Spring Web Services 2 - Part II ( Marshalling , UnMarshalling using JAXB)

In second part of the series, we will talk about how to use Marshaller's in order to automatically convert your request and response object to the SOAP Body XML (of request n response ) with the corresponding Namespace defined in the Java Source files.
Just like the first part, we will use Spring WS2- Part I we will use the same web service hosted at W3Schools.com but this time, with a very short code, as compared to Part-I .
Spring Web Services 2 makes use of a number of marshallers and unmarshallers in order to perform the tasks that include Spring OXM, Castom, Jaxb2. As JAXB 2 is the specification, so we will go by that only.

Note: Please add Jaxb libraries in your classpath while performing the action during this tutorial.

Configuration: So we will directly delve in the spring configuration (acjaxb.xml) as required for this tutorial.



    
    
    
    
        
       
            

                springwsjxb.CelsiusToFahrenheit
                springwsjxb.CelsiusToFahrenheitResponse
                
            
        
  
        
            
                
            
        
    

    
        
    

     
     
        
        
        
        
        
    



As our current configuration describes that we are using two classes CelsiusToFahrenheit and CelsiusToFahrenheitResponse present in the springws packagejxb. Here is the corresponding source code for both of them. The Source code includes the Namespace details also, it becomes very important once we need to generate the XML, in any case if we missed out the namespace the coressponding web service will not be able to parse it and will throw an exception.
CelsiusToFahrenheit.java


package springwsjxb;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 *
 * @author PankajB
 */
@XmlType(name="tem")
@XmlRootElement(name="CelsiusToFahrenheit",namespace="http://tempuri.org/")
public class CelsiusToFahrenheit {

    private int celsius;
   

    @XmlElement(name="Celsius",namespace="http://tempuri.org/")
    public int getCelsius() {
        return celsius;
    }

    public void setCelsius(int celsius) {
        this.celsius = celsius;
    }
}

CelsiusToFahrenheitResponse.java



package springwsjxb;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name="CelsiusToFahrenheitResponse",namespace="http://tempuri.org/")
public class CelsiusToFahrenheitResponse {

    private int result;

    //CelsiusToFahrenheitResult is the name of the XML Element which is being returned from the Web service and belongs to namespace "http://tempuri.org/"

    @XmlElement(name="CelsiusToFahrenheitResult",namespace="http://tempuri.org/")
    public int getResult() {
        return result;
    }

    public void setResult(int result) {
        this.result = result;
    }

}

As per the requirements of the web service, we had kept only those variables here in our objects.
Now our Main.java file which will be responsible for invoking the web service and give us the response object.
Main.java


package springwsjxb;

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.SoapMessage;

/**
 *
 * @author PankajB
 */
public class Main {

   
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        try
        {
   // Load the spring web service configuration file
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springwsjxb/acjaxbb.xml");
        WebServiceTemplate webServiceTemplate = (WebServiceTemplate)applicationContext.getBean("webServiceTemplate");       
        // Creating the Request Object and setting it properties
        CelsiusToFahrenheit celsiusToFahrenheit=new CelsiusToFahrenheit();
        celsiusToFahrenheit.setCelsius(100);
        // Invoking the web service and getting the response back.
                CelsiusToFahrenheitResponse c=(CelsiusToFahrenheitResponse)webServiceTemplate.marshalSendAndReceive(celsiusToFahrenheit,new WebServiceMessageCallback() {

     // Setting the SOAP Action
        public void doWithMessage(WebServiceMessage message) {
            ((SoapMessage)message).setSoapAction("http://tempuri.org/CelsiusToFahrenheit");
        }
    });


               System.out.println("THE RESPONSE From Web Service IS "+ c.getResult());

        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

}
The code is pretty self explanatory, except at one point where we need to cast the object to our corresponding response, as the web service template does not provide any way to specify in the parameter list about the kind of response object we are expecting to receive.

Once it gets executed we will receive the result on to our console.
Someetimes our web servie is accessible only through HTTPS protocol, however the tried to just replace the URI Of my web services template, and it seems to work without any added configurtion. It might be possible that we need to install the certificate on our system either manually or through a software called PORTECLE.
So that's all for the time.
 In the next part we will see how to create a web service through spring ws 2 and implement the same endpoint for both SOAP 1.1 & SOAP 1.2 i.e. we will not bother about whether client is invoking through text/xml or application/soap+xml (SOAP 1.2). Really Spring WS makes it so easy.
 Thanks Arjen Postuma.


Tuesday, March 1, 2011

Spring Web Services 2 - Part I

This is an introductory article, in a three part series which illustrates the use of Spring Web Services in a Java/J2EE Application.
All the Examples has been created by using Spring 3, Spring Web Services 2, JAX-WS, JAXB 2.2, and wsdl4j.jar

This Part will simplify the process of invoking a simple web service located at w3schools.com ( to convert a temperature from Celsius to Fahrenheit ).
Web Service URL is : http://www.w3schools.com/webservices/tempconvert.asmx 
WSDL is : http://www.w3schools.com/webservices/tempconvert.asmx?wsdl

Note: Please use SOAP UI, an excellent tool for testing the web services.

So as per Spring WS Configuration, we will create a WebServiceTemplate which follows the same pattern as of JDBCTemplate, RestTemplate etc
So our application context (ac.xml) file in this case is:



    
    
    
     
     
        
        
       
    



Now we have to construct two Classes:
1. This Class will represent the java representation of the SOAP REQUEST BODY with all the tags defined through JAXB 2.2 framework. CelsiusToFahrenheit.java 

package springws;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 *
 * @author PankajB
 * namespace represent the namespace of the elements, plz see wsdl for greater information
 * CelsiusToFahrenheit is the Root Element name
 */
@XmlType(name="tem")
@XmlRootElement(name="CelsiusToFahrenheit",namespace="http://tempuri.org/")
public class CelsiusToFahrenheit {

    private int celsius;
   

    @XmlElement(name="Celsius",namespace="http://tempuri.org/")
    public int getCelsius() {
        return celsius;
    }

    public void setCelsius(int celsius) {
        this.celsius = celsius;
    }
}




2. This class will represent the response that is coming from the Web service. CelsiusToFahrenheitResponse.java


package springws;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name="CelsiusToFahrenheitResponse",namespace="http://tempuri.org/")
public class CelsiusToFahrenheitResponse {

    private int result;


    @XmlElement(name="CelsiusToFahrenheitResult",namespace="http://tempuri.org/")
    public int getResult() {
        return result;
    }

    public void setResult(int result) {
        this.result = result;
    }
}



Now the Final Part will be is to load the xml file and perform the invocation. The Source Code is easy to read, hence does not contain much comments (Sorry for this guys!!!)

Main.java
package springws;

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.SoapMessage;

/**
 *
 * @author PankajB
 */
public class Main {

   
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        try
        {
   // getting the file present in springws package
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springws/ac.xml");
  // get the Bean
        WebServiceTemplate webServiceTemplate = (WebServiceTemplate)applicationContext.getBean("webServiceTemplate"); 
  // Create the Marshaller, so that we can generate the request SOAP Body XMl
        JAXBContext jc= JAXBContext.newInstance(CelsiusToFahrenheit.class);
        Marshaller m=jc.createMarshaller();
  // Creating the Request Object
        CelsiusToFahrenheit celsiusToFahrenheit=new CelsiusToFahrenheit();
        celsiusToFahrenheit.setCelsius(10);
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

        StringWriter wr=new StringWriter();
  // Marshalling the object to the writer
        m.marshal(celsiusToFahrenheit, wr);
        System.out.println(wr.toString());
        StringWriter finalResponseWriter = new StringWriter();
  // Creating the Source and Result object, that will contain the corressponding REUQEST & RESPONSE.
        StreamSource webServiceInput = new StreamSource(new StringReader(wr.toString()));
        StreamResult webServiceOutput = new StreamResult(finalResponseWriter);
  // Invoking the Web Service
        webServiceTemplate.sendSourceAndReceiveToResult(webServiceInput,new WebServiceMessageCallback() {

         // This is very much required, since we need to set the ACTION as defined in the WSDL. ( Since a web service can contain multiple options
        public void doWithMessage(WebServiceMessage message) {
   // Please see the WSDL for more details.
            ((SoapMessage)message).setSoapAction("http://tempuri.org/CelsiusToFahrenheit");
        }
    }, webServiceOutput);
 // This line, will print the Response to the Console
            System.out.println(finalResponseWriter.toString());
          
   // This will simply unmarshal the xml response in the Java Object (for easy handling of response)
            JAXBContext jaxc=JAXBContext.newInstance(CelsiusToFahrenheitResponse.class);
            CelsiusToFahrenheitResponse tr=(CelsiusToFahrenheitResponse)jaxc.createUnmarshaller().unmarshal(new StringReader(finalResponseWriter.toString()));
            System.out.println(tr.getResult()  + "  is the result");
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

}
Once the following will be executed, we will see the response on our console.

In the next two parts we will see the following:
Part 2: Making use of JAXB Marshalling & UnMarshalling to automate the above manual marshalling through coding.
Part 3: How to create a web service through Spring WS and provide a simple end point to support both SOAP 1.1 & SOAP 1.2
So, stay tuned.