Below are a few Glassfish JMS admin commands and some useful OpenMQ commands (to query, purge and list) which can be be used in scripts to perform tasks as opposed to performing the same procedures via the Glassfish or OpenMQ admin consoles:
Glassfish JMS Commands
To clear previous JMS setup
asadmin delete-jms-resource <JMS_TOPIC_RESOURCE_NAME>
asadmin delete-jms-resource <JMS_TOPIC_CONNECTION_FACTORY_NAME>
To create the JMS Topic Connection factory
asadmin create-jms-resource --restype=javax.jms.TopicConnectionFactory --property transaction-support=LocalTransaction --description="JMS Topic Connection Factory." <JMS_TOPIC_CONNECTION_FACTORY_NAME>
To create the JMS Topic resource
asadmin create-jms-resource --restype=javax.jms.Topic --description="JMS Topic" <JMS_TOPIC_RESOURCE_NAME>
To list JMS resources
asadmin list-jms-resources
OpenMQ Commands
Query the JMS resources
imqcmd query bkr -b <host>:<port> -passfile <password file> -u <user name>
Purge the Topic (or Queue)
imqcmd purge dst -f -passfile <password file> -n <topic name> -t t -b <host>:<port> -u <user name>
List the message rate and packet flow
imqcmd list dst -passfile <password file> -b <host>:<port> -u <user name>
An example output from the list command would be:
Listing all the destinations on the broker specified by:
-------------------------
Host Primary Port
-------------------------
localhost 7676
-----------------------------------------------------------------------------------------------------------------
Name Type State Producers Consumers Msgs
Total Wildcard Total Wildcard Count Remote UnAck Avg Size
-----------------------------------------------------------------------------------------------------------------
JMSTopic Topic RUNNING 0 0 1 0 0 0 0 0.0
mq.sys.dmq Queue RUNNING 0 - 0 - 171 0 0 5273.322
Random mutterings, but mainly reminders for myself, on all things Java,
Monday, 7 March 2011
Thursday, 3 March 2011
Pessimistic and Optimistic Locking in JPA 2.0
A most welcome addition to JPA 2.0 was the introduction of pessimistic locking. This allows the entity manager (or query) to lock a database record thereby preventing other transactions from changing the same record. This will ensure data consistency but at a performance cost so for each entity you need to ask yourself what is the chance of contention?
Let's look at the two approaches in more detail:
Optimistic Locking
Optimistic locking is the preferred approach when modifying entities that are infrequently updated. Optimistic locking can be explicit as will be shown later or implicit by using a version attribute. A version attribute is an attribute which has been annotated with @Version. This attribute will then get incremented when the transaction commits. The below class shows an example of its usage:
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceContext;
import javax.persistence.Version;
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final int ID_LENGTH=36;
@Id
@Column(length = ID_LENGTH)
private String id;
@Version
private int versionNumber;
// etc ...
}
There are two optimistic lock modes:
Pessimistic Locking
Pessimistic locking can be applied to all entities regardless of whether they have a version attribute or not.
There are three pessimistic lock modes:
There are many different ways to implement a pessimistic or optimistic lock using the Entity Manager and Query interfaces as shown in the example classes below:
import java.io.Serializable;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Named("customerManager")
@ApplicationScoped
public class CustomerManager implements Serializable {
private static final long serialVersionUID = 1L;
@PersistenceContext
private EntityManager entityManager;
public void setName(String id, String name) {
Customer customer =
entityManager.find(Customer.class, id);
entityManager.lock(customer,
LockModeType.OPTIMISTIC_FORCE_INCREMENT);
customer.setName(name);
}
public Customer findCustomer(String id) {
return entityManager.find(Customer.class, id,
LockModeType.OPTIMISTIC);
}
public Customer
applyBankCharges(String id, Double charges) {
Customer customer =
entityManager.find(Customer.class, id);
customer.setBalance(customer.getBalance() - charges);
if (customer.getBalance() < 0.0) {
// overwrite changes and return the refreshed
// and locked object
entityManager.refresh(customer,
LockModeType.PESSIMISTIC_WRITE);
}
return customer;
}
public List<Customer>
findCustomerByPartialName(String partialName) {
Query query = entityManager.createQuery(
"SELECT c FROM Customer c WHERE c.name" +
" LIKE :partialName").
setParameter("partialName", partialName);
query.setLockMode(
LockModeType.PESSIMISTIC_FORCE_INCREMENT);
return query.getResultList();
}
}
import javax.persistence.Entity;
import javax.persistence.LockModeType;
import javax.persistence.NamedQuery;
@Entity
@NamedQuery(name = "findByNameQuery",
query = "SELECT c FROM Customer c WHERE c.name LIKE :name",
lockMode = LockModeType.PESSIMISTIC_FORCE_INCREMENT)
public class Customer extends BaseEntity {
private String name;
private Double balance;
public void setName(String name) {
this.name = name;
}
public Double getBalance() {
return this.balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
Finally, a word on exceptions. If a PessimisticLockException or an OptimisticLockException are thrown then the transaction is rolled back as they are both RuntimeExceptions.
Let's look at the two approaches in more detail:
Optimistic Locking
Optimistic locking is the preferred approach when modifying entities that are infrequently updated. Optimistic locking can be explicit as will be shown later or implicit by using a version attribute. A version attribute is an attribute which has been annotated with @Version. This attribute will then get incremented when the transaction commits. The below class shows an example of its usage:
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceContext;
import javax.persistence.Version;
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final int ID_LENGTH=36;
@Id
@Column(length = ID_LENGTH)
private String id;
@Version
private int versionNumber;
// etc ...
}
There are two optimistic lock modes:
- OPTIMISTIC or READ - locks the entity when the transaction reads it for entities wit a version
- OPTIMISTIC_FORCE_INCREMENT or WRITE - locks the entity when the transaction reads it for entities with a version and increments the version attribute
Pessimistic Locking
Pessimistic locking can be applied to all entities regardless of whether they have a version attribute or not.
There are three pessimistic lock modes:
- PESSIMISTIC_READ - locks the entity when the transaction reads it and allows reads from other transactions
- PESSIMISTIC_WRITE - locks the entity when the transaction updates it but does not allow reads, updates or deletes from other transactions
- PESSIMISTIC_FORCE_INCREMENT - locks the entity when the transaction reads it and increments the version attribute (if present)
import java.io.Serializable;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Named("customerManager")
@ApplicationScoped
public class CustomerManager implements Serializable {
private static final long serialVersionUID = 1L;
@PersistenceContext
private EntityManager entityManager;
public void setName(String id, String name) {
Customer customer =
entityManager.find(Customer.class, id);
entityManager.lock(customer,
LockModeType.OPTIMISTIC_FORCE_INCREMENT);
customer.setName(name);
}
public Customer findCustomer(String id) {
return entityManager.find(Customer.class, id,
LockModeType.OPTIMISTIC);
}
public Customer
applyBankCharges(String id, Double charges) {
Customer customer =
entityManager.find(Customer.class, id);
customer.setBalance(customer.getBalance() - charges);
if (customer.getBalance() < 0.0) {
// overwrite changes and return the refreshed
// and locked object
entityManager.refresh(customer,
LockModeType.PESSIMISTIC_WRITE);
}
return customer;
}
public List<Customer>
findCustomerByPartialName(String partialName) {
Query query = entityManager.createQuery(
"SELECT c FROM Customer c WHERE c.name" +
" LIKE :partialName").
setParameter("partialName", partialName);
query.setLockMode(
LockModeType.PESSIMISTIC_FORCE_INCREMENT);
return query.getResultList();
}
}
import javax.persistence.Entity;
import javax.persistence.LockModeType;
import javax.persistence.NamedQuery;
@Entity
@NamedQuery(name = "findByNameQuery",
query = "SELECT c FROM Customer c WHERE c.name LIKE :name",
lockMode = LockModeType.PESSIMISTIC_FORCE_INCREMENT)
public class Customer extends BaseEntity {
private String name;
private Double balance;
public void setName(String name) {
this.name = name;
}
public Double getBalance() {
return this.balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
Finally, a word on exceptions. If a PessimisticLockException or an OptimisticLockException are thrown then the transaction is rolled back as they are both RuntimeExceptions.
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.
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.
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);
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);
Tuesday, 8 February 2011
JPA Entity Lifecycle Callback and Listener Annotations
Within the javax.persistence package there are annotations that may be applied to methods of an entity or a mapped superclass to specify that the annotated method must be called for a certain lifecycle event. A lifecycle event being either: Persist, Update, Remove or Load. and each event has a Pre or Post event.
Listeners can also have methods annotated with a lifecycle event. Listeners can be used to hold the business logic that would otherwise be in the entities (or another layer.) This enables the entities to be just POJOs. It also means that the testing of the business logic can be done in isolation.
Below is an example of using lifecycle events to populate an id var and also populate a lastUpdated var, in a mapped superclass:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final int ID_LENGTH=36;
@Id
@Column(length = ID_LENGTH)
private String id;
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
private Date dateCreated;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
// constructor(s)
@PreUpdate
public void updateLastModified () {
lastModified = new Date();
}
@PrePersist
protected void generateUUID() {
id = java.util.UUID.randomUUID().toString();
dateCreated = new Date();
lastModified = new Date();
}
.....
}
An simple example of using a Listener could be where an entity has a transient var that needs to be populated after the entity has been persisted, updated or loaded.
public class AvailableCreditListener {
@PostLoad
@PostPersist
@PostUpdate
public void calculateAvailableCredit{Account account) {
account.setAvailableCredit(
account.getBalance().add(
account.getOverdraftLimit()));
}
}
The entity class would be annotated with @EntityListeners:
@EntityListeners({AvailableCreditListener.class})
public class AccountEntity extends BaseEntity {
private BigDecimal balance;
private BigDecimal overdraftLimit;
@Transient
private BigDecimal availableCredit;
// getters and setters
}
Finally, instead of annotations, an XMl mapping file can be used and deployed with the application to specify default listeners. (This mapping file is referenced by the persistence.xml file.) But an entity can use the @ExcludeDefaultListeners annotation if it does not want to use the default listeners.
Listeners can also have methods annotated with a lifecycle event. Listeners can be used to hold the business logic that would otherwise be in the entities (or another layer.) This enables the entities to be just POJOs. It also means that the testing of the business logic can be done in isolation.
Below is an example of using lifecycle events to populate an id var and also populate a lastUpdated var, in a mapped superclass:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final int ID_LENGTH=36;
@Id
@Column(length = ID_LENGTH)
private String id;
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
private Date dateCreated;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
// constructor(s)
@PreUpdate
public void updateLastModified () {
lastModified = new Date();
}
@PrePersist
protected void generateUUID() {
id = java.util.UUID.randomUUID().toString();
dateCreated = new Date();
lastModified = new Date();
}
.....
}
An simple example of using a Listener could be where an entity has a transient var that needs to be populated after the entity has been persisted, updated or loaded.
public class AvailableCreditListener {
@PostLoad
@PostPersist
@PostUpdate
public void calculateAvailableCredit{Account account) {
account.setAvailableCredit(
account.getBalance().add(
account.getOverdraftLimit()));
}
}
The entity class would be annotated with @EntityListeners:
@EntityListeners({AvailableCreditListener.class})
public class AccountEntity extends BaseEntity {
private BigDecimal balance;
private BigDecimal overdraftLimit;
@Transient
private BigDecimal availableCredit;
// getters and setters
}
Finally, instead of annotations, an XMl mapping file can be used and deployed with the application to specify default listeners. (This mapping file is referenced by the persistence.xml file.) But an entity can use the @ExcludeDefaultListeners annotation if it does not want to use the default listeners.
@ExcludeDefaultListeners
@Entity
public class AccountEntity extends BaseEntity {
....
}
Tuesday, 1 February 2011
Loose Coupling using CDI
A key to n-tier architectures is notion of loose coupling and CDI enables this between classes in Java EE 6.
By using the @Inject annotation, you can specify an injection point in a class. The below example uses the annotation on a setter method.
@Inject
public void setCustomer(Customer customer) {
this.customer = customer;
}
It could equally be used on a field as below:
@Inject
Customer customer;
It is possible that more than one implementation of the Customer class exists so to resolve this problem at runtime, qualifiers are used. They provide the ability to 'qualify' what gets injected. an example qualifier is below:
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Qualifier
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TradeCustomerQualifier {
}
The TradeCustomer class would be enhanced to use the qualifier by way of an annotation.
@TradeCustomerQualifier
@Named("tradeCustomer")
@ApplicationScoped
public class TradeCustomer extends Customer {
......
}
An example of using the qualifier is below:
@Inject
public void setCustomer(@TradeCustomerQualifier Customer customer) {
this.customer = customer;
}
Finally, in order for CDI to bootstrap, there'll need to be a beans.xml file in the META-INF folder (for jars and in the WEB-INF for web applications.)
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
By using the @Inject annotation, you can specify an injection point in a class. The below example uses the annotation on a setter method.
@Inject
public void setCustomer(Customer customer) {
this.customer = customer;
}
It could equally be used on a field as below:
@Inject
Customer customer;
It is possible that more than one implementation of the Customer class exists so to resolve this problem at runtime, qualifiers are used. They provide the ability to 'qualify' what gets injected. an example qualifier is below:
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Qualifier
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TradeCustomerQualifier {
}
The TradeCustomer class would be enhanced to use the qualifier by way of an annotation.
@TradeCustomerQualifier
@Named("tradeCustomer")
@ApplicationScoped
public class TradeCustomer extends Customer {
......
}
An example of using the qualifier is below:
@Inject
public void setCustomer(@TradeCustomerQualifier Customer customer) {
this.customer = customer;
}
Finally, in order for CDI to bootstrap, there'll need to be a beans.xml file in the META-INF folder (for jars and in the WEB-INF for web applications.)
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Subscribe to:
Posts (Atom)