Monday 12 December 2011

Multiple Eclipse Workspaces

When working with multiple workspaces in Eclipse, it is very helpful to know which IDE window is using which workspace. You can show workspace location in your Eclipse title bar by passing the -showlocation parameter to the Eclipse executable or modifying the eclipse.ini config file.

Wednesday 12 October 2011

Using Oracle XMLQuery and XML-24509: (Error) Duplicated Definition

Recently I was asked to investigate how static configuration data between multiple deployments of an enterprise application could be managed more easily. The requirement was to be able to extract the required information form an Oracle database so that configuration data between two deployments could be compared. Additionally, if the configuration data along with the relationship between tables could be captured, it would streamline the process of populating a database for a new deployment.

Oracle provides some nice features for working with XML. There is the option of using PL/SQL and the DBMS_XMLQUERY package or the corresponding OracleXMLQuery Java class.

The OracleXMLQuery class provides an API to retrieve the results of an SQL query as XML. Therefore, I developed a class which would use the OracleXMLQuery and write the results to a file. The query was defined in a Spring context file and injected along with the javax.sql.DataSource. Now it came to the task of writing a test to validate if my class was working. Following the popular approach of testing with Spring and JUnit, I created a test class and a corresponding test context file.


Unfortunately the test would not execute and instead I was presented with the following error:
<Line 43, Column 57>: XML-24509: (Error) Duplicated definition for: 'identifiedType'

<Line 60, Column 28>: XML-24509: (Error) Duplicated definition for: 'beans'

<Line 157, Column 34>: XML-24509: (Error) Duplicated definition for: 'description'

<Line 169, Column 29>: XML-24509: (Error) Duplicated definition for: 'import'

<Line 191, Column 28>: XML-24509: (Error) Duplicated definition for: 'alias'

<Line 220, Column 33>: XML-24509: (Error) Duplicated definition for: 'beanElements'

<Line 235, Column 44>: XML-24509: (Error) Duplicated definition for: 'beanAttributes'

<Line 510, Column 43>: XML-24509: (Error) Duplicated definition for: 'meta'

<Line 518, Column 35>: XML-24509: (Error) Duplicated definition for: 'metaType'

The problem was that the Oracle XDK which provides the XML Java classes makes use of the Oracle xmlparserv2.jar file to parse XML. This has issues with parsing Spring XSD files and produces the above errors. Spring relies on Apache xerces library. Therefore the solution in this case was to configure Spring not to use the Oracle xml parser via a system property.


System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");


Adding the above code to my test class' @BeforeClass method fixed the issue.

Friday 19 August 2011

Set up JBPM – Oracle - Tomcat 6

1. Add transaction and Resource information after 'WatchedResource' line in TOMCAT_HOME/conf/context.xml as shown below. This step is required to create a datasource in tomcat

<WatchedResource>WEB-INF/web.xml</WatchedResource>

<Transaction factory ="bitronix.tm.BitronixUserTransactionObjectFactory" />

<Resource name="jdbc/testDS1" auth="Container" type="javax.sql.DataSource" maxActive="15" maxIdle="2" maxWait="10000" logAbandoned="true"username="username" password="password" driverClassName=" oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@yourdatabaseurl:port:db"/>


2. Create 'resource.properties' in TOMCAT_HOME/conf and add following as shown below.
resource.ds1.className=bitronix.tm.resource.jdbc.lrc.LrcXADataSource
resource.ds1.uniqueName=jdbc/testDS1
resource.ds1.minPoolSize=0
resource.ds1.maxPoolSize=5
resource.ds1.driverProperties.driverClassName=oracle.jdbc.OracleDriver

3. Copy oracle ojdbc14.jar into the ‘tomcat/lib’ and ‘jbpm-installer/runtime’ folder for oracle driver.

4. Modify hibernate.cfg.xml in TOMCAT_HOME\webapps\gwt-console-server\WEB-INF\classes\META-INF to include Oracle connection details and comment the h2 details.
Note: Its better to change into the WAR (gwt-console-server.war) itself so can be deployed on different instances.

Replace the session-factory tag from below
<session-factory>
<!-- h2 Database connection settings -->
<!--property name="connection.url">jdbc:h2:file:/NotBackedUp /data/mydb</property-->
<!--
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.url">jdbc:h2:tcp://localhost/~/test</ property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
-->
<!-- Oracle Database connection settings -->
<property name="connection.driver_class"& gt;oracle.jdbc.OracleDriver</property>
<property name="connection.url">
jdbc:oracle:thin:@yourdatabaseurl:port:db</property>
<property name="connection.username">username</property>
<property name="connection.password">password</property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</ property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread& lt;/property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class"& gt; org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">false</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="AuditLog.hbm.xml"/>
</session-factory>

5. Make sure the jbpm-human-task-5.0-SNAPSHOT.jar is in TOMCAT_HOME/webapps/gwt-console-server/WEB-INF/lib (5.1 full installer zip contains this jar in the gwt-console-server web-inf lib) OR if not present then download and copy jbpm-human-task-5.0-SNAPSHOT.jar to TOMCAT_HOME/webapps/gwt-console-server/WEB-INF/lib

6. Make sure the jbpm-bam-5.0-SNAPSHOT.jar is in TOMCAT_HOME/webapps/gwt-console-server/WEB-INF/lib (5.1 full installer zip contains this jar in the gwt-console-server web-inf lib) OR if not present then download and copy copy jbpm-bam-5.0-SNAPSHOT.jar to TOMCAT_HOME/webapps/gwt-console-server/WEB-INF/lib

7. Add or Replace oracle connection details in persistence.xml of TOMCAT_HOME\webapps\gwt-console-server\WEB-INF\classes\META-INF.

<persistence-unit name="org.jbpm.persistence.jpa">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jdbc/testDS1</jta-data-source>
<mapping-file>META-INF/JBPMorm.xml</mapping-file>
<class>org.drools.persistence.session.SessionInfo</class>
<class>org.drools.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.processinstance.WorkItemInfo</class>
<class>org.jbpm.process.audit.ProcessInstanceLog</class>
<class>org.jbpm.process.audit.NodeInstanceLog</class>
<class>org.jbpm.process.audit.VariableInstanceLog</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.max_fetch_depth" value="3"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>


8. Modify the jbpm-bam-5.1.0.Final.jar/hibernate.cfg.xml of to add the oracle connection properties. Replace the session-factory with the following xml.

<session-factory>
<!-- h2 Database connection settings -->
<!--property name="connection.url">jdbc:h2:file:/NotBackedUp/data/mydb</property-->
<!--
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.url">jdbc:h2:tcp://localhost/~/test</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
-->
<!-- Oracle Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">
jdbc:oracle:thin:@yourdatabaseurl:port:db</property>
<property name="connection.username">username</property>
<property name="connection.password">password</property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">false</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="AuditLog.hbm.xml"/>
</session-factory>


9. Make sure all the above modified jars (jbpm-bam-5.1.0.Final.jar, jbpm-human-task-5.1.0.Final.jar) are also present/copied to the jbpm-installer/runtime folder before running ‘ant start.human.task’ from jbpm-installer. On running the task, all the tables will be created in oracle schema.

Set up the JBPM suite on tomcat 6

1. Download tomcat 6 from apache website tomcat-6.0.32.zip and unzip in a folder.
2. Change the TOMCAT_HOME, CATALINA_HOME env variable pointing to the new tomcat folder.
3. Download jbpm-5.1.0.Final-installer-full.zip from jboss site unzip into a folder.
a. Go into the lib folder, which will have 6 more files in it. 2 wars and 6 zip files.
4. copy 2 wars into the tomcat-home/webapps. Rename of war is required as per below names otherwise all the applications does not start properly.
a. Rename the designer-1.0.0.052-jboss.war to designer.war
b. Rename the guvnor-distribution-wars-5.2.0.Final-jboss-as-5.1.war to drools-guvnor.war
c. Unzip jbpm-5.1.0.Final-gwt-console.zip into the webapps. This zip contains 2 more wars.
d. Rename jbpm-gwt-console-server-5.1.0.Final.war to gwt-console-server.war
e. Rename jbpm-gwt-console-5.1.0.Final.war to jbpm-console.war
5. At this point, if tomcat is restarted, the complete suit is ready to work on. Go to tomcat manager (localhost:8080) and try launching drools-guvnor and jbpm-console. All the functionality does not work with IE so use firefox. It may ask to install google frame. Accept and continue.
6. To login into jbpm console, create few users into tomcat-users.xml as below.






Thursday 18 August 2011

Drop all tables from schema

Run below code to drop all the tables from schema.

BEGIN

FOR i IN (SELECT table_name FROM user_tables)
LOOP
EXECUTE IMMEDIATE('DROP TABLE ' || user || '.' || i.table_name || ' CASCADE CONSTRAINTS');
END LOOP;
END;

Friday 15 July 2011

Mercurial: .hgignore and eclipse

When using synchronization, you will want mercurial to ignore/not track certain files. otherwise, it is very difficult to check what files to commit from eclipse synchronize view.

Create .hgignore file to your root and add all the files which are required to ignore by mercurial.
example: .hgignore
#-----------------------------------------------------------------------
# Add an entry for .settings folder to ignore
#------------------------------------------------------------------------
syntax: regexp
^common/\.settings$

#----------------------------------------------------------------------------
# Add an entry for target folder to ignore when creating a new module.
# This will ignore all the class files.
#----------------------------------------------------------------------------
syntax: regexp
^common/target$

#-------------------------------------
# Use global syntax
#-------------------------------------
syntax: glob
.obj
.exe

Wednesday 13 July 2011

Error Solution: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role; no session or session was closed

The problem is when I run the test, it failed on the last assert with the “org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role; no session or session was closed” error.

My DAO method and its test is as follows:


DAOImpl:
public List getActivityUIFields(ActivityType activityType) {
List rv = null;
try {
Query query = em.createNamedQuery("mobileWallet.uiConfigDAO.getUIConfigData");
query.setParameter("activityType", activityType.getValue());
rv = query.getResultList();
} catch (NoResultException nre) {
// No record found.
}
return rv;
}

DAOImplTest:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/dao-test.xml" })
@Transactional
public class UIConfigDAOImplTest {
@Autowired
private EntityManagerFactory entityManagerFactory;

private EntityManager em;

@Autowired
private UIConfigDAO uiConfigDAO;

@Before
public void setUp() {
em = entityManagerFactory.createEntityManager();
}

@After
public void tearDown() throws Exception {
}

@Test
public void testGetUIConfigData() {

List list = uiConfigDAO.getActivityUIFields(ActivityType
.getInstance("SELF"));
Assert.assertNotNull(list);
Assert.assertFalse(list.isEmpty());
Assert.assertEquals("FirstName", list.iterator().next().getValue());
Assert.assertNotNull(list.iterator().next().toString());
}
}


Its bit surprising that Assert.assertEquals("FirstName", list.iterator().next().getValue()); got passed but test failed on the next line.

Reason: Once the Hibernate Session is closed anything that hasn't been loaded, can't be loaded without reattaching your entity to a Hibernate Session. The Session will end once the statement has been executed. If I debug the test method, list has all the values and it works if getValue() is called. But fails on accessing the collection.

Solution: The whole test should be running into a transaction. The easiest way is adding @Transactional at class level. This will put the whole test method in one transaction.

We faced the similar situation when business service was called and the reason was the business service was not under one transaction. The method should be in a transaction where the retrieved collection is used. Always keep the business service in a transaction.

Tuesday 12 July 2011

Specifying parameters for Mockito unit testing

Recently we faced a problem where we wanted to unit-test a service operation using mock objects for dependencies, but the objective of the test was to ensure the operation correctly instantiated an entity from the parameters so that it was ready to be persisted.


@Transactional
public User registerUser(Map<UIField,String> details) {
  User user = new User();
  List<UIField> identifierTypes = referenceDataService.
    getUserIdentifierTypeList();
  Set<UserDetail> userDetails = new HashSet<UserDetail>();
  Set<UserIdentifier> userIdentifiers = new   
    HashSet<UserIdentifier>();
  for(UIField key :details.keySet()) {   
    if(isIdentifierField(key,identifierTypes)) {            userIdentifiers.add(createUserIdentifier(key,
        details.get(key)));
    } else {
      userDetails.add(createUserDetail(key,details.get(key)));
    }
  }
  user.setUserIdentifiers(userIdentifiers);
  user.setUserDetails(userDetails);
  UserStatus status = referenceDataService.
    getUserStatus("REGISTERED");
  user.setUserStatus(status);
  return userService.createUser(user);
}
Listing 1

As listing 1 shows, the registerUser operation instantiates a User object and populates its properties using the parameters passed to the operation. Now we would like the unit-test to ensure this User instance is correctly constructed and its properties correctly populated so that the userService can persist the user. Listing 2 shows the unit-test.


@Test
public void testRegisterUser() {
//need a map of the data that is being submitted for registration.
  Map<UIField,String> data = new HashMap<UIField,String>();
  addUIField(data,"MSISDN","07987654321");
  addUIField(data,"Firstname","Joe");
  addUIField(data,"Lastname","Bloggs");        
  List<UIField> idFieldList = new ArrayList<UIField>();
  idFieldList.add(createUIField("MSISDN"));
     
  Mockito.when(referenceDataService.getUserIdentifierTypeList()).
    thenReturn(idFieldList);
  UserStatus userStatus = new UserStatus();
  userStatus.setValue("REGISTERED");

  Mockito.when(referenceDataService.getUserStatus("REGISTERED")).
    thenReturn(userStatus);
  registrationService.registerUser(data);            
  ArgumentCaptor<User> argument =
    ArgumentCaptor.forClass(User.class);

  Mockito.verify(userService).createUser(argument.capture());
  User user = argument.getValue();
  Assert.assertNotNull(user);

  Assert.assertEquals(user.getUserStatus().getValue(),"REGISTERED");
  Assert.assertEquals(1, user.getUserIdentifiers().size());
  Assert.assertEquals(2, user.getUserDetails().size());
}
 Listing 2

The userService and referenceDataService are mock objects which get injected into the RegistrationService. The concern was how to retrieve the user instance from the registerUser operation so that it can be inspected. For this, Mockito provides the ArgumentCaptor class and its use is highlighted in bold showing how to retrieve the User instance and then carry out further assertions.