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.

Wednesday 16 February 2011

Subversion Reminders - Branching, Merging and Rollbacks

Below are a few Subversion reminders on how to branch, merge and rollback changes.

To create a branch from the HEAD of the trunk:

svn copy http://<server>/<svn repo>/<svn project>/branches/<branch name>

You can add a -m <comment> onto the operation to provide a reason for the branch.

To delete a branch:

svn delete http://<server>/<svn repo>/<svn project>/branches/<branch name>

If for some reason you want to include changes from the trunk post-branch, then you can use the merge command from the branch root directory. A useful precursor to this is to use the dry run argument as below:

svn merge --dry-run -r2602:HEAD http://<server>/<svn repo>/<svn project>/trunk

NOTE: 2602 is non-inclusive i.e. revisions merged will start from 2603 onwards.

Now do the actual merge:

svn merge -r2602:HEAD http://<server>/<svn repo>/<svn project>/trunk

No doubt there will be conflicts so once resolved in your favourite IDE, check the changes in.

The above commands assume you noted down the revision number (2602) when branching. If not known, then from the branch root directory, execute the below command:

svn log -v --stop-on-copy

To merge branch changes into the trunk, the merge command can be used in similar fashion from the trunk root directory:

svn merge -r2602:HEAD http://<server>/<svn repo>/<svn project>/branches/<branch name>

If you want to rollback commited changes, then you can use the merge command with the -r argument:

svn merge -r10:9 http://<server>/<svn repo>/<svn project>/trunk

To rollback a commit that took place a while ago and others that have been committed since, use the merge command again:

svn merge -r7:5 http://<server>/<svn repo>/<svn project>/trunk

NOTE: Note that this will revert revisions 7 and 6. Revision 5 is the target revision that we want to keep.

Many thanks to Rob Legg of Asset Source Ltd for the above summary.

Monday 14 February 2011

Unit testing managed beans using Mockito

Mockito is a powerful testing framework which is ideal for unit testing managed beans. It allows developers to mock existing classes thereby enabling the behaviour of those classes to be manipulated by the developer depending upon what the aim of the test is.

A simple managed bean which is to be tested is below:

import java.io.Serializable;
import java.math.BigDecimal;
import javax.annotation.Named;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

@Named("customerManager")
@ApplicationScoped
public class CustomerManager implements Serializable {
    private static final long serialVersionUID = 1L;
    private AccountManager accountManager;

    @Inject
    public void setAccountManager(

        final AccountManager accountManager) {
        this.accountManager = accountManager;
    }

    public boolean isInCredit() {
        boolean inCredit = false;
        BigDecimal currentAccountBalance = 

            this.accountManager.getBalance(AccountType.CURRENT);
        BigDecimal depositAccountBalance = 

            this.accountManager.getBalance(AccountType.DEPOSIT);
        BigDecimal accountBalance = 

            new BigDecimal(currentAccountBalance.doubleValue())
                .add(depositAccountBalance);
        if (accountBalance.compareTo(BigDecimal.ZERO) > 0) {
            inCredit = true;
        }
        return inCredit;
    }

}



The test class for CustomerManager could include various scenarios depending on what is returned by the AccountManager class. An example test class is below:

import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.times;
import static junit.framework.Assert.assertTrue;

public class CustomerManagerTest {

    private CustomerManager customerManager;
    private AccountManager mockAccountManager = 

        mock(AccountManager.class);

    @Before
    public void setUp() throws Exception {
        customerManager = new CustomerManager();
        customerManager.setAccountManager(mockAccountManager);
    }

    @Test
    public void testPositiveBalance() {
        BigDecimal positiveAmount  = new BigDecimal(1000.00);
        when(mockAccountManager.getBalance(((AccountType)any())))

            .thenReturn(positiveAmount);
        boolean inCredit = customerManager.isInCredit();
        assertTrue(inCredit);
        verify(mockAccountManager, times(2))

            .getBalance((AccountType)any());
    }

}


The when is mocking calls to the AccountManager getBalance method for any given parameter of type AccountType. As the method should have been called twice (once for CURRENT and once for DEPOSIT), the verify call can determine the number of getBalance method invocations. A more fine grained test equivalent of the above could be:

    @Test
    public void testPositiveBalance() {
        BigDecimal positiveAmount  = new BigDecimal(1000.00);
        when(mockAccountManager.getBalance(AccountType.CURRENT))

            .thenReturn(positiveAmount);    
        when(mockAccountManager.getBalance(AccountType.DEPOSIT))

            .thenReturn(positiveAmount);
        boolean inCredit = customerManager.isInCredit();
        assertTrue(inCredit);
        verify(mockAccountManager)

            .getBalance(AccountType.CURRENT);
        verify(mockAccountManager)

            .getBalance(AccountType.DEPOSIT);
    }


The argument matchers are more explicit in the above and could also return different results if needs be.

It is also possible to mock consecutive calls to a method so the same argument can generate a different return object. Using the getBalance example, the first call could return 1000.0 and the next 2000.0.

        BigDecimal positiveAmount1  = new BigDecimal(1000.00);
        BigDecimal positiveAmount2  = new BigDecimal(2000.00);
        when(mockAccountManager.getBalance(((AccountType)any())))

            .thenReturn(positiveAmount1)
            .thenReturn(positiveAmount2);

It is also possible to partially mock real objects using the spy method. If a real AccountManager object had been created and set, then the getBalance method could have be stubbed as shown below:

import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static junit.framework.Assert.assertTrue;

public class CustomerManagerTest {

    private CustomerManager customerManager;
    private AccountManager accountManager = new AccountManager();

    @Before
    public void setUp() throws Exception {
        customerManager = new CustomerManager();
    }

    @Test
    public void testPositiveBalance() {
        AccountManager spyAccountManager = spy(accountManager);
        customerManager.setAccountManager(spyAccountManager);
        BigDecimal postiveAmount  = new BigDecimal(1000.00);
        when(spyAccountManager.getBalance(((AccountType)any())))

            .thenReturn(postiveAmount);
        boolean inCredit = customerManager.isInCredit();
        assertTrue(inCredit);
        verify(spyAccountManager, times(2))

            .getBalance((AccountType)any());
    }

}


There are many more features to the Mockito framework than described here (throwing exceptions, stubbing void methods, etc..) and http://mockito.org/ is a good starting point.

Finally, you can also use annotations to declare mock objects. An alternative to using

    private AccountManager mockAccountManager = 
        mock(AccountManager.class);

would be to use the @Mock annotation as below:

    @Mock
    private AccountManager mockAccountManager;


and in the setup method initialise them by the following command:

    MockitoAnnotations.initMocks(this);