You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 26 Next »

Introduction

The purpose of this tutorial is not at all to teach you on SOA but to draw your attention on points that the developer(s)/deployer(s) will be confronted during the design/development and release management phases.

Designing a Service Oriented Architecture seems very obvious for most of us but implies that different parameters are taken into account :

  • Identification of the layers of the application,
  • Definition of the services,
  • Granularity (what are the boundaries of a service, ...),
  • Dependency with libraries,
  • Testing and debugging strategies,
  • Deployment procedure,
  • Infrastructure

Some of the points mentioned are particular to SOA world like granularity and definition of service boundaries but others are mostly found in all IT projects. This is really important to keep them in your head because they will impact the project life cycle, quality of the deliverable, efficiency of the team and project duration/cost.

In this second part of the tutorial we will investigate some of the points mentioned and applied them to a real application. The application will be designed around different components (= bundles in the OSGI jargon) and deployed into ServiceMix Kernel. ServiceMix Kernel or SMXKNL is an OSGI platform created top of Apache Felix OSGI server, integrating Spring Dynamic Modules to facilitate the build of Spring application and PAX components who provide tools managing deployment of component and web support.

For more information about the projects/frameworks mentioned, I recommend that you have a look on their respective web site.

Here is a picture of the report incident application that this tutorial will cover :

To summarize, the application is listening for incidents coming from web service or files. According to the origin, the content (= incidents) are transformed into their corresponding objects using for the CSV file, a new camel component : camel-bindy and for the Web Service camel-cxf component. Each message transformed is placed in a queue handled by ActiveMQ engine. All the messages (containing the objects) are next processed by a Bean service who will (with the help of injection of dependency provided by Spring) save the incidents in a DB using Spring and Hibernate frameworks.
A small Apache Wicket web application running in Jetty Web server provide to the users an screen to consult the incidents created.

Remark : A bundle in the OSGI world represents component made by developer. For more info about OSGI, I recommend to have a look on OSGI web site

The project has been cut into the following components :

Maven project name = artifactId

Description

Is it a bundle ?

reportincident.activemq

configuration file of the ActiveMQ engine

yes

reportincident.camelqueueservice

configuration file of the camel-activemq component

yes

reportincident.db

generator of the script DB

no

reportincident.features

features provisioning file containing our bundles dependencies

no

reportincident.model

model layer

yes

reportincident.persistence

hibernate persistence layer; bundle

yes

reportincident.routing

camel routing

yes

reportincident.service

spring service layer

yes

reportincident.web

apache wicket module

yes

reportincident.webservice

CXF web service generator

yes

As you can see, some are considered as OSGI bundles and others no. An important point to mention here concerns the granularity : each layer of our application will be deployed as separate bundles. This will facilitate the maintenance and release management. Of course, you can argue that the granularity is too small. SOA is not an exact science and depending of the size of the application, the team in charge to develop, release management procedure this cutting will be redefined. You can imagine that the parameters used to configure Hibernate and Spring are bundles together instead inside the persistence. Service bundle could be split into several bundles; one by service type, ... There are no rules of thumb except that the project must be manageable and maintainable.

Prerequisites

This tutorial uses:

Note: The sample project can be downloaded, see the resources section.

Step 1 : Initial Project Setup

Different way exist to create maven project. For the basic project like db, we have used the archetype 'simple' with the command followed by mvn eclipse:eclipse in the folder created :

 
mvn archetype:create -DartifactId=simple -DgroupId=org.apache.camel.example -DartifactId=reportincident.model -Dversion=1.0-SNAPSHOT
cd reportincident.db
mvn eclipse:eclipse

For the OSGI bundles different approaches are available depending on the tools that you prefer to use :

But for the purpose of this tutorial, we have used the spring archetype and made some cleanup/modifications. Why this choice, simply because it allows to create :

  • all the folders required including also the MANIFEST,
  • Eclipse files

in one step.

To create one of the tutorial project, you can follow the procedure described here

1) Execute maven command in your Unix/Dos console :

mvn archetype:create -DarchetypeGroupId=org.springframework.osgi -DarchetypeArtifactId=spring-osgi-bundle-archetype 
-DarchetypeVersion=1.2.0 -DgroupId=org.apache.camel.example -DartifactId=reportincident.model -Dversion=1.0-SNAPSHOT

2) Import the generated eclipse project in Eclipse workspace
3) Delete readme.txt, build.properties and template.mf files
3) Enable dependency management (see sonatype site).

otherwise import the content of the unzipped file in your workspace. You will gain time.

Step 2 : Develop model layer

It is time now to begin serious things. One of the most important part of a project (if not the most important) concerns the design of the model.
The reportincident model is really simple because it only contains one class that we will use to map information with the database, CSV file and from web screens.

Here is the definition of the incident class (that you can create in the reportincident.model project (directory src/main/java/org/apache/camel/example/reportincident/model or use the code imported)

import java.io.Serializable;

public class Incident implements Serializable{

	private static final long serialVersionUID = 1L;
	
	protected long incidentId;

	protected String incidentRef;
	
	protected Date incidentDate;
	
	protected String givenName;
	
	protected String familyName;
	
	protected String summary;
	
	protected String details;
	
	protected String email;
	
	protected String phone;
	
	protected String creationUser;
	
	protected Date creationDate;
	
	
	public long getIncidentId() {
		return incidentId;
	}

	public void setIncidentId(long incidentId) {
		this.incidentId = incidentId;
	}
	
	public String getIncidentRef() {
		return incidentRef;
	}

	public void setIncidentRef(String incidentRef) {
		this.incidentRef = incidentRef;
	}

	public Date getIncidentDate() {
		return incidentDate;
	}

	public void setIncidentDate(Date incidentDate) {
		this.incidentDate = incidentDate;
	}

	public String getGivenName() {
		return givenName;
	}

	public void setGivenName(String givenName) {
		this.givenName = givenName;
	}

	public String getFamilyName() {
		return familyName;
	}

	public void setFamilyName(String familyName) {
		this.familyName = familyName;
	}

	public String getSummary() {
		return summary;
	}

	public void setSummary(String summary) {
		this.summary = summary;
	}

	public String getDetails() {
		return details;
	}

	public void setDetails(String details) {
		this.details = details;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getCreationUser() {
		return creationUser;
	}

	public void setCreationUser(String creationUser) {
		this.creationUser = creationUser;
	}

	public Date getCreationDate() {
		return creationDate;
	}

	public void setCreationDate(Date creationDate) {
		this.creationDate = creationDate;
	}

}

Step 3 : Map model layer with CSV file (camel-bindy)

To facilitate the work of the modeler, we will use the incident class not only to persist the information in the database but also to read or generate Comma Separate Value file. To map the content of the class with a CSV file, we have used a new Camel component : camel-bindy. Like its name suggests, camel-bindy is a binding framework (similar to JAXB) to map non structured information with Java class using annotations. The current version supports CSV fields and key-value pairs (e.g. Financial FIX messages) but will be extended in the future to support Fixed Length format, ....

So, we will modify our existing class to add @Annotations required to map its content. This is very trivial to do and will be done in two steps :

1) Add CSVRecord annotation

This annotation will help camel-bindy to discover what is the parent class of the model and which separator is used to separate the fields. If required, you can also use the property 'skipFirstLine' to skip the first line of your CSV file

import org.apache.camel.dataformat.bindy.annotation.CsvRecord;

@CsvRecord(separator =",")
public class Incident implements Serializable{
...
}

2) Add DataFields annotations

For each of the CSV field that you want to bind with your model, you must add the @DataField annotation with its position. This is not the only property available and you can also add 'pattern' property to by example define the pattern of your Date field.

import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;

@CsvRecord(separator =",")
public class Incident implements Serializable{

    @DataField(pos = 0)
    protected String incidentRef;
	
    @DataField(pos = 1, pattern = "dd-mm-yyyy")
    protected Date incidentDate;
	
    @DataField(pos = 2)
    protected String givenName;
	
    @DataField(pos = 3)
    protected String familyName;
	
    @DataField(pos = 4)
    protected String summary;

    @DataField(pos = 5)
    protected String details;
	
    @DataField(pos = 6)
    protected String email;
	
    @DataField(pos = 7)
    protected String phone;

...
}

Step 4 : Map model layer with DB (Hibernate)

To map our model with the database, we will use the ORM framework Hibernate. Annotation can also be used since the last version of Hibernate but to avoid to overload our class and reduce its readability, we will use the old way using a XML file describing the mapping between the model and the database

<hibernate-mapping schema="REPORT">
	<class name="org.apache.camel.example.reportincident.model.Incident" table="T_INCIDENT">
		<meta attribute="extends">Abstract</meta>
		<id name="incidentId" column="INCIDENT_ID"  type="long">
			<generator class="native" />
		</id>
		
		<property column="INCIDENT_REF" name="incidentRef" length="55" type="string" />
		<property column="INCIDENT_DATE" lazy="false" length="8" name="incidentDate" type="timestamp" />
		<property column="GIVEN_NAME" length="35" name="givenName" type="string" />
		<property column="FAMILY_NAME" length="35" name="familyName" type="string" />
		<property column="SUMMARY" length="35" name="summary" type="string" />
		<property column="DETAILS" length="255" name="details" type="string" />
		<property column="EMAIL" length="60" name="email" type="string" />
		<property column="PHONE" length="35" name="phone" type="string" />

		<property column="CREATION_DATE" generated="never" lazy="false" name="creationDate" type="timestamp" />
		<property column="CREATION_USER" generated="never" lazy="false" name="creationUser" type="string" />
	</class>
</hibernate-mapping>

Remark : This file Incident.hbm.xml must be created in the directory src\main\resources\META-INF\org\apache\camel\example\reportincident\model\Incident.hbm.xml of the project reportincident.model.

Step 6 : Database creation

To create the data base model, we will use hibernate maven plugin and hibernate.cfg.xml file. This file who will instruct the plugin to generate the SQL script and create table T_Incident.

Here is the content of the hibernate.cfg.xml (that you must create in the folder src/config of hibernate.db

<!-- MySQL DB -->

<hibernate-configuration>
	<session-factory name="reportincident">
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///report</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
		<property name="hibernate.connection.password" />
		<property name="hibernate.show_sql">true</property>
		
		<!-- mapping files -->
        <mapping resource="META-INF/org/apache/camel/example/reportincident/model/Incident.hbm.xml"/>
		
	</session-factory>
</hibernate-configuration>

The pom.xml file of your reportincident.db project must be modified like this :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.apache.camel.example</groupId>
	<artifactId>reportincident.db</artifactId>
	<packaging>jar</packaging>
	<name>Report Incident DB </name>
	<version>1.0-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.apache.camel.example</groupId>
			<artifactId>reportincident.model</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>

			<!-- Hibernate  plugin -->
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>hibernate3-maven-plugin</artifactId>
				<version>2.2</version>
				<configuration>
					<components>
						<component>
							<name>hbm2ddl</name>
						</component>
					</components>
					<componentProperties>
						<drop>true</drop>
						<create>true</create>
						<format>true</format>
						<configurationfile>/src/config/hibernate.cfg.xml</configurationfile>
						<outputfilename>db_reportincident_create_hsqldb.sql</outputfilename>
					</componentProperties>
				</configuration>
				<dependencies>
					<dependency>
						<groupId>mysql</groupId>
						<artifactId>mysql-connector-java</artifactId>
						<version>5.1.6</version>
					</dependency>
				</dependencies>

				<executions>
					<execution>
						<phase>process-classes</phase>
						<goals>
							<goal>hbm2ddl</goal>
						</goals>
					</execution>
				</executions>

			</plugin>

		</plugins>
	</build>
</project>

Remarks :

  • dependency with reportincident.model project must be added because the plugin requires the file Incident.hbm.xml to generate the script/db
  • if you prefer to use another DB instead of MySql, change the dependency in the pom.xml, hibernate.connection.driver_class and hibernate.connection.url in the cfg file

To create the table + SQL script, simply launch the mvn clean install command in the folder of reportincident.db

Step 7 : Add persistence layer and Spring service

The

#Resources

  • No labels