Thursday, 17 February 2011

Generating Web Services from WSDLs using Maven and deploying to Glassfish

This blog post aims to cover generating Java classes from WSDLs using Maven and it also covers a problem with web annotations when deploying to an app server.

There are many different ways to generate Java classes for a given wsdl file (and associated xsds). One of those ways is to use the JAX-WS wsimport tool. Amongst the classes that the tool can generate are the service endpoint interface and the service class.

When using Maven, you can use the jaxws-maven-plugin and the wsimport goal. The plugin will read a WSDL file (from the /src/wsdl directory unless otherwise specified via the wsdlLocation tag) and generate the Java classes. The plugins sections from a pom.xml is shown below:


       <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>1.10</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                        <configuration>
                            <wsdlFiles>
                                <wsdlFile>Customer.wsdl</wsdlFile>
                            </wsdlFiles>
                            <staleFile>

             ${project.build.directory}/jaxws/stale/Customer.stale
                            </staleFile>
                        </configuration>
                        <id>wsimport-generate-Customer</id>
                        <phase>generate-sources</phase>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>javax.xml</groupId>
                        <artifactId>webservices-api</artifactId>
                        <version>${javax.xml.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>com.sun.xml.bind</groupId>
                        <artifactId>jaxb-xjc</artifactId>
                        <version>${jaxb-xjc.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>com.sun.xml.ws</groupId>
                        <artifactId>jaxws-rt</artifactId>
                        <version>${jaxws-rt.version}</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <sourceDestDir>

       ${project.build.directory}/generated-sources/jaxws-wsimport
                    </sourceDestDir>
                    <xnocompile>true</xnocompile>
                    <verbose>true</verbose>
                    <extension>true</extension>
                    <catalog>

                        ${basedir}/src/jax-ws-catalog.xml
                    </catalog>
                </configuration>
            </plugin>
        </plugins>



The generated web service class will look like the class below:

@WebService(
    name = "Customer", 
    targetNamespace = "http://<package>/Customer")
@SOAPBinding(

    parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface Customer {

    /**
     *
     * @param body
     */
    @WebMethod(

        operationName = "AddCustomer", 
        action = "http://<package>/Customer/AddCustomer")
    public void addCustomer(
        @WebParam(name = "AddCustomer", 

            targetNamespace = "http://<package>/Customer",
            partName = "body") AddCustomer body);
}



Given the above interface, a class can be written to implement the above generated interface as shown below:

@WebService(name = Customer.NAME,
            targetNamespace = Customer.TARGET_NAMESPACE,
            portName = Customer.PORT_NAME,
            serviceName = Customer.SERVICE_NAME,
            wsdlLocation = Customer.WSDL_LOCATION_URL)
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@BindingType(value = Customer.SOAP12_OVER_HTTP)
@SchemaValidation()
public class CustomerWebService implements Customer {

    static final String NAME = "Customer";
    static final String TARGET_NAMESPACE 
        = TARGET_NAMESPACE_URL + NAME;
    static final String PORT_NAME = NAME + "SOAP";
    static final String SERVICE_NAME = NAME;
    static final String WSDL_LOCATION_URL 
        = WSDL_LOCATION_PATH + NAME + ".wsdl";
    static final String SOAP12_OVER_HTTP 
        = "http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/";

    @Override
    @WebMethod(operationName = "AddCustomer")
    public void addCustomer(
        @WebParam(name = "AddCustomer", 
            targetNamespace = "http://<package>/Customer",
            partName = "body") AddCustomer body) {

        // do something
    }

}


Having built an ear file containing the web service, you can then deploy it. To deploy to eg Glassfish v3.0.1, you can use the following command:

asadmin --host <server> --port <port> --interactive=false --echo=true --terse=true deploy
--name customer-ear --force=false --precompilejsp=false --verify=false --enabled=true --generatermistubs=false --availabilityenabled=false
--keepreposdir=false --keepfailedstubs=false --logReportedErrors=true --help=false ./customer-ear.ear


One point to note is that the below implementation of the addCustomer method would compile...

    @Override
    public void addCustomer(AddCustomer body) {

        // do something
    }

... but it would not inherit the Web annotations and would result in the below error when deploying the ear:

com.sun.enterprise.admin.cli.CommandException: remote failure: Exception while loading the app : java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.apache.catalina.LifecycleException: javax.servlet.ServletException
Exception while invoking class com.sun.enterprise.web.WebApplication start method : java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.apache.catalina.LifecycleException: javax.servlet.ServletException

The Web annotations need to be explicitly replicated in the concrete class.

3 comments:

  1. Thank you for the info. It sounds pretty user friendly. I guess I’ll pick one up for fun. thank u


    Web Services Chennai

    ReplyDelete
  2. Thanks, right to the point!

    ReplyDelete

Note: only a member of this blog may post a comment.