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.

2 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.