Service Oriented Architecture

SOA is short for Service Oriented Architecture. SOA is basically an architecture where there are a set of services which are typically exposed as a set of web services by one or more applications and can be invoked by webservice clients. It’s a readymade framework. For e.g.you could have a front end interface for internet banking which would invoke a remote webservice to authenticate the user on login, another webservice to get a list of accounts and yet another webservice to get account details etc. So there is this bank of services which is the SOA layer that provides information to external clients services eg. as web services.
The primary motive is to decouple the producer and consumer of information so that one is agnostic about the internals of the other.
It is usually a set of webservices, but not always. For example you could have have a layer of services that are invoked by Flex (Flash UI) from a browser where flex is a thick client like applet/flex. (Thin client is html/javascript).
So in our case, the thick browser client is invoking services on the app server.
SOA is not really anything different, it’s just a design pattern or architecture. One example would be EJBs where you have a bunch of EJBs providing services and the client code will lookup an EJB and invoke a method on it.You could deploy updates to the EJBs without impacting the client code as long as the method signature doesn’t change, but mostly SOA is a set of webservices; that’s the most common usage. This is another way of decoupling business implementation from the client code. The client is only interested in getting access to that business functionality leaving the implementation to the SOA developers.
That’s all there is to SOA – the implementation is quite varying. In that sense most webservices are SOA, but SOA might be a host of webservices.
If you search for SOA on the internet you’ll find a lot of literature, but a lot of them won’t make sense because all vendors tout their own frameworks so instead of stating the theory they dive into the detailed implementations.
Here are some links…
IBM’s http://www-01.ibm.com/software/solutions/soa/
microsoft http://msdn.microsoft.com/en-us/library/aa480021.aspx
oracle http://www.oracle.com/us/technologies/soa/index.html
Most of the literature maybe marketing stuff.
Here is a generic explanation of SOA
http://javaboutique.internet.com/tutorials/serv_orient/

Using JBPM for workflow

JBPM terminology -> process-definition, node, state, transition, process instance, process instance id, actionhandler.

This example demostrates the use of JBPM as a workflow engine. I have used the jars in the package jbpm-jpdl-suite-3.2.3.zip available on the sourceforge site. In addition to this you will need the mysql jar containing the mysql jdbc driver.
I have used a standalone example to demonstrate the use of jbpm while in real practise it would require a full fledged GUI where state changes in ItemRequest would take place due to user actions. The various states that a RequestItem object can go into is shown in the diagram below and described in processdefinition.xml as a workflow.

processdefinition.xml



	
		
	
	
		
			
				Created
			
			
				Created
			
		
		
	
	
		
			
				Submitted
			
			
				Submitted
			
		
		
		
	
	
		
			
				Approved
			
			
				Approved
			
		
		
		
		
	
	
		
			
				Rejected
			
			
				Rejected
			
		
		
	
	
		
			
				Cancelled
			
			
				Cancelled
			
		
		
	
	
		
			
				Archived
			
			
				Archived
			
		
	

JBPM requires an initial setup in the database. JBPM tables are created in the mysql table when the following line(commented) in the main class is executed.

new JBPMTest().setupSchema();//run this only once for setup
This is a one time execution and should be run only once or whenever there is a change to processdefinition.xml.
Database jbpmappdb need to be created in mysql before running the above line.

The main class below:

package com.mattiz.jbpm;

import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import com.mattiz.jbpm.dto.ItemRequest;
import com.mattiz.jbpm.handler.CounterHandler;

public class JBPMTest {
	public static void main(String args[]) {
		// new JBPMTest().setupSchema();//run this only once for setup
		new JBPMTest().testHelloWorldProcess();
	}

	public void testHelloWorldProcess() {
		ItemRequestController ic = new ItemRequestController();
		ItemRequest itemRequest = new ItemRequest();
		itemRequest.setId("1");
		itemRequest.setName("My Item Request");
		itemRequest.setStatus("Submitted");
		ic.createItemRequest(itemRequest);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		String oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Approved");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Cancelled");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		System.out.println("===================================");

		itemRequest = new ItemRequest();
		itemRequest.setId("2");
		itemRequest.setName("My Second Item Request");
		itemRequest.setStatus("Submitted");
		ic = new ItemRequestController();
		ic.createItemRequest(itemRequest);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Approved");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Rejected");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		System.out.println("===================================");

		itemRequest = new ItemRequest();
		itemRequest.setId("3");
		itemRequest.setName("My Third Item Request");
		itemRequest.setStatus("Submitted");
		ic = new ItemRequestController();
		ic.createItemRequest(itemRequest);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Approved");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		System.out.println("===================================");

		itemRequest = new ItemRequest();
		itemRequest.setId("4");
		itemRequest.setName("My Fourth Item Request");
		itemRequest.setStatus("Submitted");
		ic = new ItemRequestController();
		ic.createItemRequest(itemRequest);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Approved");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		oldStatus = itemRequest.getStatus();
		itemRequest.setStatus("Rejected");
		ic.updateItemRequest(itemRequest, oldStatus);
		System.out.println("PENDING  "
				+ CounterHandler.itemRequestsPendingCounter);
		System.out.println("CONFIRMED "
				+ CounterHandler.itemRequestsConfirmedCounter);
		System.out.println("===================================");
		System.out.println("===================================");
	}

	public void setupSchema() {
		JbpmConfiguration config = JbpmConfiguration
				.parseResource("jbpm.cfg.xml");
		ProcessDefinition processDefinition = ProcessDefinition
				.parseXmlResource("processdefinition.xml");
		// create schema.
		config.createSchema();
		JbpmContext context = config.createJbpmContext();
		// deploy process definition.
		try {
			context.deployProcessDefinition(processDefinition);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			context.close();
		}
	}
}

The hibernate configuration file hibernate.cfg.xml below:






	
		<!-- JDBC connection properties -->
		
			org.hibernate.dialect.MySQLDialect
		
		
			com.mysql.jdbc.Driver
		
		
			jdbc:mysql://localhost:3306/jbpmappdb
		
		admin
		admin
		<!-- JDBC connection properties (end) -->

		<!-- Simple memory-only cache -->
		
			org.hibernate.cache.HashtableCacheProvider
		

		<!-- logging properties -->
		true
		true

		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		

		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	

The jbpm configuration file jbpm.cfg.xml below:


	
		
		<!-- Logging Service (begin) -->
		
		<!-- Logging Service (end) -->
		
		
		
		
	

	

	<!-- configuration resource files pointing to default configuration files
		in jbpm-{version}.jar -->
	
	
	
	
	
	
	

	<!-- make sure the block size matches the length in ByteArray.hbm.xml -->
	
	
	
	
	
	

	
		
			
		
		
			
		
		
			
		
		
			
		
		
			
		
		
			
		
		
			
		
		
			
		
		
			
		
	

The domain class is ItemRequest.java

package com.mattiz.jbpm.dto;

public class ItemRequest {
	private String id;
	private String name;
	private String status;
	/** JBPM mapping. */
	private Long processInstanceId;

	public Long getProcessInstanceId() {
		return processInstanceId;
	}

	public void setProcessInstanceId(Long processInstanceId) {
		this.processInstanceId = processInstanceId;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}
}

Note that it defines a variable processInstanceId required for JBPM.

The class that makes JBPM work below:

package com.mattiz.jbpm.workflow;

import java.util.ArrayList;
import java.util.List;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ProcessInstance;
import com.mattiz.jbpm.dto.ItemRequest;

public class JBPMWorkFlow {
	JbpmConfiguration config = JbpmConfiguration.parseResource("jbpm.cfg.xml");

	public Long createItemRequestFlow(ItemRequest ItemRequest) {
		JbpmContext context = config.createJbpmContext();
		ProcessInstance pi = null;
		try {
			pi = context.newProcessInstance("Mattiz_Process_Flow");
			pi.getContextInstance().setVariable("ItemRequestId",
					ItemRequest.getId());
			pi.signal();
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			context.close();
		}
		return pi.getId();
	}

	public List makeTransition(Long processInstanceId,
            String transitionName) throws RuntimeException {
        JbpmContext context = config.createJbpmContext();
        List transitionNames = new ArrayList();
        try {
            ProcessInstance pi = context.getProcessInstance(processInstanceId);
            List leavingTransitions = pi.getRootToken().getNode()
                    .getLeavingTransitions();
            for (Transition t : leavingTransitions) {
                transitionNames.add(t.getName());
            }
            System.out.println("Choices are " + leavingTransitions);
            System.out.println("ACTION IS--->" + transitionName);
            pi.signal(transitionName);
        } catch (JbpmException e) {
            throw new RuntimeException(e);
        } finally {
            context.close();
        }
        return transitionNames;
    }

	public void makeCounterChanges(Long processInstanceId, String oldStatus)
			throws RuntimeException {
		JbpmContext context = config.createJbpmContext();
		try {
			ProcessInstance pi = context.getProcessInstance(processInstanceId);
			pi.getContextInstance().setVariable("oldStatus", oldStatus);
		} catch (JbpmException e) {
			throw new RuntimeException();
		} finally {
			context.close();
		}
	}
}

We define two JBPM handler classes.

CounterHandler keeps track of the number of ItemRequest objects in submitted state and in the approved state using two static counters whose values are printed out in the main class whenever a state change occurs.

package com.mattiz.jbpm.handler;

import java.util.HashMap;
import java.util.Map;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;

public class CounterHandler implements ActionHandler {
	private static Map actionForStatusChange = new HashMap();
	public static int itemRequestsPendingCounter;
	public static int itemRequestsConfirmedCounter;
	// status is passed on implicitly from the processdefinition xml
	private String status;
	static {
		actionForStatusChange.put("NEW" + "_" + "Submitted",
				"addToItemRequestsPending");
		actionForStatusChange.put("Submitted" + "_" + "Approved",
				"addToItemRequestsConfirmedAndSubtractFromItemRequestsPending");
		actionForStatusChange.put("Approved" + "_" + "Rejected",
				"subtractFromItemRequestsConfirmed");
		actionForStatusChange.put("Submitted" + "_" + "Rejected",
				"subtractFromItemRequestsPending");
		actionForStatusChange.put("Approved" + "_" + "Cancelled",
				"subtractFromItemRequestsConfirmed");
	}

	public void execute(ExecutionContext executionContext) throws Exception {
		String oldStatus = (String) executionContext.getContextInstance()
				.getVariable("oldStatus");
		if (oldStatus == null) {
			oldStatus = "NEW";
		}
		String action = actionForStatusChange.get(oldStatus + "_" + status);
		if (null != action) {
			if (action.equals("addToItemRequestsPending")) {
				itemRequestsPendingCounter++;
			} else if (action.equals("addToItemRequestsConfirmed")) {
				itemRequestsConfirmedCounter++;
			} else if (action.equals("subtractFromItemRequestsConfirmed")) {
				itemRequestsConfirmedCounter--;
			} else if (action.equals("subtractFromItemRequestsPending")) {
				itemRequestsPendingCounter--;
			} else if (action
					.equals("addToItemRequestsConfirmedAndSubtractFromItemRequestsPending")) {
				itemRequestsPendingCounter--;
				itemRequestsConfirmedCounter++;
			} else if (action.equals("subtractFromItemRequestsPending")) {
				itemRequestsPendingCounter--;
			}
		}
	}
}

StatusHandler is also a listener which is triggered whenever status change occurs. In this app it does not do anything, but in a real app can be used to update the status of ItemRequest in the database.


package com.mattiz.jbpm.handler;

import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;

public class StatusHandler implements ActionHandler {
	private String status;

	public void execute(ExecutionContext executionContext) throws Exception {
		String itemRequestId = (String) executionContext.getContextInstance()
				.getVariable("ItemRequestId");
		System.out.println("Item " + itemRequestId);
		executionContext.getContextInstance().setVariable("status", status);
		// do something important like update the status in the database using
		// the itemRequestId
	}
}

ItemRequestController is the interface to the frontend and calls methods in the workflow service class:

package com.mattiz.jbpm;

import java.util.List;
import com.mattiz.jbpm.dto.ItemRequest;
import com.mattiz.jbpm.workflow.JBPMWorkFlow;

public class ItemRequestController {
    JBPMWorkFlow workflowService = new JBPMWorkFlow();

    public List createItemRequest(ItemRequest itemRequest) {
        Long processInstanceId = workflowService
                .createItemRequestFlow(itemRequest);
        itemRequest.setProcessInstanceId(processInstanceId);
        workflowService.makeCounterChanges(processInstanceId, null);
        List transitions = workflowService.makeTransition(
                itemRequest.getProcessInstanceId(), itemRequest.getStatus());
        return transitions;
    }

    public List updateItemRequest(ItemRequest itemRequest,
            String oldStatus) {
        Long processInstanceId = itemRequest.getProcessInstanceId();
        workflowService.makeCounterChanges(processInstanceId, oldStatus);
        List transitions = workflowService.makeTransition(
                processInstanceId, itemRequest.getStatus());
        return transitions;
    }
}

The output of the main class looks something like this:

Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 1
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 1
PENDING 0
CONFIRMED 1
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Cancelled
Item 1
PENDING 0
CONFIRMED 0
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 2
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 2
PENDING 0
CONFIRMED 1
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Rejected
Item 2
PENDING 0
CONFIRMED 0
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 3
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 3
PENDING 0
CONFIRMED 1
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 4
PENDING 1
CONFIRMED 1
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 4
PENDING 0
CONFIRMED 2
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Rejected
Item 4
PENDING 0
CONFIRMED 1
===================================
===================================

Download the full app here

Easy Debugging!

Many a times one needs to debug an application by printing out the state of a java bean or multiple java beans with complex inner variables which may themselves be java beans or complex data types such as arraylists. This task is simplified a lot using the gson utility.

The following standalone application implements two java beans that print out their state by extending a base class that overrides Java Object class’ toString() method.

In the toString() method gson is used to print out formatted state of the java bean that inherits it.

Bean.java


package com.mattiz.bean;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Bean {
	@Override
	public String toString() {
		Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting()
				.create();
		return gson.toJson(this);
	}
}

SampleBean.java

package com.mattiz.bean;

import java.util.ArrayList;
import java.util.List;

public class SampleBean extends Bean {
	private String name;
	private int id;
	private List names;
	private SampleBean2 sampleBean2 = new SampleBean2();

	public SampleBean2 getSampleBean2() {
		return sampleBean2;
	}

	public void setSampleBean2(SampleBean2 sampleBean2) {
		this.sampleBean2 = sampleBean2;
	}

	public SampleBean() {
		this.name = "SampleBean";
		this.id = 1;
		this.names = new ArrayList();
		this.names.add("One");
		this.names.add("Two");
		this.names.add(null);

	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public List getNames() {
		return names;
	}

	public void setNames(List names) {
		this.names = names;
	}
}

SampleBean2.java

package com.mattiz.bean;

public class SampleBean2 extends Bean {
	private String address1;
	private String address2;
	private String city;

	public String getAddress1() {
		return address1;
	}

	public void setAddress1(String address1) {
		this.address1 = address1;
	}

	public String getAddress2() {
		return address2;
	}

	public void setAddress2(String address2) {
		this.address2 = address2;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}
}

The main class that tests this functionality:

package com.mattiz;

import com.mattiz.bean.SampleBean;
import com.mattiz.bean.SampleBean2;

public class TestJson_2_4 {
	public static void main(String[] args) {
		SampleBean2 sp2 = new SampleBean2();
		SampleBean sp = new SampleBean();
		sp2.setAddress1("MJ street");
		sp.setSampleBean2(sp2);
		System.out.println("SampleBean : " + sp);
		System.out.println("SampleBean2 : " + sp2);
	}
}

Output looks something like this:

SampleBean : {
"name": "SampleBean",
"id": 1,
"names": [
"One",
"Two",
null
],
"sampleBean2": {
"address1": "MJ street",
"address2": null,
"city": null
}
}
SampleBean2 : {
"address1": "MJ street",
"address2": null,
"city": null
}

The only jar I am using is gson-1.7.1.jar which has this debug utility.