Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Friday, October 26, 2007

jboss seam and jbpm async nodes

In a jboss seam project i wanted to use async nodes in a jbpm workflow. You can add "async=true" to any node in you jbpm process definition. This makes the workflow continue in the background so your user interface does not have to wait for the complete workflow to complete.

I accomplished this by starting the JbpmThreadServlet by adding in the web.xml:


<!-- Jpbpm Thread listener which will do async continuations & Timer -->

<servlet>
<servlet-name>JbpmThreadsServlet</servlet-name>
<servlet-class>org.jbpm.web.JbpmThreadsServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JbpmThreadsServlet</servlet-name>
<url-pattern>/jbpmthreads</url-pattern>
</servlet-mapping>



When your web application starts, jbpm will spawn off a thread that will continue your workflow. This thread is also being used for sending out email notifications of task nodes.

A problem that rises here is that your action handlers don't have access to the seam contexts. To solve this problem I made a abstract action handler which will startup the seam lifecycle from a such an actionHandler if it does not exist. This code comes from the seam 2.0.0 framework which actually support this. I haven't tried it out still.

The code for the actionHandler is:



import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.core.BusinessProcess;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.exe.TaskInstance;

public abstract class SeamContextualActionHandler implements ActionHandler {

abstract public void executeInSeam(ExecutionContext context)
throws Exception;

public void execute(final ExecutionContext context) throws Exception {

try {
if (Contexts.isEventContextActive()
|| Contexts.isApplicationContextActive()) //not sure about the second bit (only needed at init time!)
{

} else {
Lifecycle.beginCall();
try {
initProcessAndTask(context);
executeInSeam(context);
} finally {
Lifecycle.endCall();
}
}
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static void initProcessAndTask(ExecutionContext context) {
BusinessProcess businessProcess = BusinessProcess.instance();
businessProcess.setProcessId(context.getProcessInstance().getId());
TaskInstance taskInstance = context.getTaskInstance();
if (taskInstance != null) {
businessProcess.setTaskId(taskInstance.getId());
}
}

}





Your actionHandlers should extend this class and implement the executeInSeam() method instead of the execute method from the ActionHandler interface.

Monday, October 15, 2007

postgresql and jdbc/hibernate batch updates

A thing I always forget is to turn of batch updates when using postgresql db. Postgresql is a nice open source SQL database. It is less known then mysql but it offers true ACID semantics. Anyway, it is my favorite choice for RDBMS. But you need to turn off batch updates if you are using hibernate or JPA ( which is also implemented by hibernate ). So make sure you turn it off with the property

hibernate.jdbc.batch_size and set the value to 0.

Otherwise you get an exception sounding like:


09:00:46,017 ERROR [AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.ejb.AbstractEntityManagerImpl$1.beforeCompletion(AbstractEntityManagerImpl.java:515)
at org.jboss.tm.TransactionImpl.doBeforeCompletion(TransactionImpl.java:1491)
at org.jboss.tm.TransactionImpl.beforePrepare(TransactionImpl.java:1110)
at org.jboss.tm.TransactionImpl.commit(TransactionImpl.java:324)
at org.jboss.tm.TxManager.commit(TxManager.java:240)
...

Friday, October 12, 2007

accessing private fields

Today I learned again something new from the java reflection API. You can set private fields in a class. This comes in handy when you want to write a Unit test for a class that depends om some external services or classes;

Lets say we have:


public class ClassUnderTest {

private OtherClass otherClass;

public doSomethingWithOtherClass() {

otherClass.someFunction()
}
}


When we want to test this class we might want to Mock the OtherClass because we are not interested in the functionality of the otherClass. Our testcase has no direct access to the otherClass field so we need to use some magic to do so.

public class MyTestCase {
@Test
public void testDoSomethingWithOtherClass {
Field otherClassField = ClassUnderTest.class.getDeclaredField("otherClass"); (1)
otherClassField.setAccessible(true); (2)

ClassUnderTest classUnderTest = new ClassUnderTest();
OtherClass otherClass = createMock(OtherClass.class);
otherClass.someFunction();
otherClassField.set(classUnderTest,otherClass); (3)
replay(otherClass)
classUnderTest.doSomethingWithOtherClass();
verify(otherClass);

}
}


(1) We use the reflection api to fetch the private Field of the ClassUnderTest
(2) We make it accessible
(3) We set the value on a instance of ClassUnderTest

And we're set.

createMock() replay() and verify() are functions from EasyMock which is a verify nice framework for creating Mock objects. You should certainly take a look at it if you don't know it yet. @Test is a annotation from testng unit test framework.



Wednesday, September 12, 2007

Could not enlist in transaction on entering meta-aware object!

This errormessage provided by the Jboss j2ee server gave me a major headance. In a project we are using 2 datasources:

  • an oracle datasource being used by iBatis DAO layer
  • a postgresql datasource being used by JPA DAO layer
After invoking both DAO layers I got this nice SystemException by the JTA transactionmanager.

Investigation showed me ( a few hours later ) that the oracle datasource is not XA by default. You have to configure it to use the XADataSource. You can find a example in the jboss distribution in docs/examples/jca/oracle-xa-ds.xml