Permalink
Browse files

Adding more docs to the features and routing modules

  • Loading branch information...
gertv committed Jan 18, 2013
1 parent 50990b7 commit 297ab7a9cad9cb4cf46ad77a5f9ce1b668623395
View
@@ -74,9 +74,9 @@ We will refer to the directory that contains your Fuse ESB installation as `$ESB
Before we can start Fuse ESB, we have to make sure we configure a user we can use later on to connect to the embedded
message broker and send messages to a queue. Edit the `$ESB_HOME/etc/users.properties` file and add a line that says:
- user=password,admin
+ user=password,admin
-The syntax for this line is <userid>=<password>,<group>, so we're creating a user called `user` with a password `password`
+The syntax for this line is &lt;userid&gt;=&lt;password&gt;,&lt;group&gt;, so we're creating a user called `user` with a password `password`
who's a member of the `admin` group.
### Start Fuse ESB
@@ -103,7 +103,7 @@ Using `osgi:list` in the console, you should now see this demo's bundles at the
Open `jconsole` and connect to the running Fuse ESB Enterprise instance. If the instance is running locally, connect to
the process called `org.apache.karaf.main.Main`.
-On the MBeans tab, navigate to `org.apache.activemq` &rarr; `fusemq` &rarr; `Queue` &rarr; `Input.Orders`. Send a few
+On the MBeans tab, navigate to `org.apache.activemq` &rarr; `fusemq` &rarr; `Queue` &rarr; `Input.Flights`. Send a few
messages to the queue using the `sendTextMessage(String body, String user, String password)` operation. For the second
and third password, use the username and password you configured earlier. The first parameter will become the flight ID
in the database, so just use your imagination for that one ;)
@@ -58,4 +58,9 @@ public String getArrival() {
public void setArrival(String arrival) {
this.arrival = arrival;
}
+
+ @Override
+ public String toString() {
+ return String.format("[flight %s from %s to %s]", number, departure, arrival);
+ }
}
@@ -46,7 +46,6 @@
You can also use JNDI references to refer to services in the OSGi Service Registry, which is what we're using here.
-->
<jta-data-source>osgi:service/jdbc/transactions</jta-data-source>
- <non-jta-data-source>osgi:service/jdbc/transactions</non-jta-data-source>
<!--
Defining the entity classes that are available in this persistence unit. In our example, we only have one entity
@@ -60,6 +59,10 @@
these properties contain additional configuration for OpenJPA.
-->
<properties>
+ <!--
+ Setting ConnectionFactoryMode to managed because DataSource is enlisted in transactions automatically by Aries JTA
+ -->
+ <property name="openjpa.ConnectionFactoryMode" value="managed"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
<property name="openjpa.jdbc.DBDictionary" value="derby"/>
<property name="openjpa.Log" value="DefaultLevel=INFO"/>
View
@@ -22,6 +22,8 @@ In this example, the bundle id is 268. Using the `osgi:ls` command, we can see
* secondly, Aries JTA has added a corresponding `javax.sql.DataSource` and added the `aries.xa.aware = true` property to it to indicate an XA-aware data source
* finally, the Blueprint extender mechanism also published the Blueprint container (containg our data source bean definitions) it created
+This is what the `osgi:ls` output looks like
+
FuseESB:karaf@root> osgi:ls 268
Fuse By Example :: Transactions :: Datasource (268) provides:
View
@@ -1,46 +1,10 @@
-# Datasource bundle
-This bundle defines the JDBC `DataSource`s that will be used for accessing the relational database.
+# Features
+This project creates a features definition that can be used to install the example.
-## Blueprint
-We are using Blueprint to define the data source bean and publish it into the OSGi Service Registry. Have a look at
-`src/main/resources/OSGI-INF/blueprint/datasource.xml` for more details about how this done.
+## How it works?
+Using Maven's resource filtering mechanism, we replace the `${xyz.version}` tokens in the `src/main/resources/features.xml` file.
+Afterwards, the resulting file is published as a Maven artifact itself so you can use it from within the Fuse ESB console with
-## Checking the OSGi Service Registry
-After the bundle is started, we can use the Fuse ESB Enterprise console to look at the registered objects in the registry.
+ FuseESB:karaf@root> features:addurl mvn:org.fusesource.example.transactions/features/1.0-SNAPSHOT/xml/features
-First, find the bundle id for the bundle called "Fuse By Example :: Transactions :: Datasource" by using the `osgi:list` command.
-You can use `grep` to filter the list of bundles and quickly find the right one.
-
- FuseESB:karaf@root> osgi:list | grep Transactions
- [ 268] [Active ] [Created ] [ ] [ 60] Fuse By Example :: Transactions :: Datasource (1.0.0.SNAPSHOT)
- [ 269] [Active ] [ ] [ ] [ 60] Fuse By Example :: Transactions :: Database (1.0.0.SNAPSHOT)
- [ 270] [Active ] [ ] [Started] [ 60] Fuse By Example :: Transactions :: Routing (1.0.0.SNAPSHOT)
-
-In this example, the bundle id is 268. Using the `osgi:ls` command, we can see that this bundle is publishing 3 services:
-
-* first, there's the `javax.sql.XADataSource` that we created in our Blueprint XML file
-* secondly, Aries JTA has added a corresponding `javax.sql.DataSource` and added the `aries.xa.aware = true` property to it to indicate an XA-aware data source
-* finally, the Blueprint extender mechanism also published the Blueprint container (containg our data source bean definitions) it created
-
- FuseESB:karaf@root> osgi:ls 268
-
- Fuse By Example :: Transactions :: Datasource (268) provides:
- -------------------------------------------------------------
- datasource.name = transactions
- objectClass = javax.sql.XADataSource
- osgi.jndi.service.name = jdbc/transactions
- osgi.service.blueprint.compname = transactionsDataSource
- service.id = 414
- ----
- aries.xa.aware = true
- datasource.name = transactions
- objectClass = javax.sql.DataSource
- osgi.jndi.service.name = jdbc/transactions
- osgi.service.blueprint.compname = transactionsDataSource
- service.id = 415
- service.ranking = 1000
- ----
- objectClass = org.osgi.service.blueprint.container.BlueprintContainer
- osgi.blueprint.container.symbolicname = org.fusesource.example.transactions.datasource
- osgi.blueprint.container.version = 1.0.0.SNAPSHOT
- service.id = 416
+Inside the features file, you'll find a definition for feature called `transactions-openjpa-demo`.
@@ -1,6 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) Red Hat, Inc.
+
+ Red Hat licenses this file to you under the Apache License, version
+ 2.0 (the "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+-->
<features name="transactions-example-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
+ <!--
+ This features defines the list of everything we need to run our little transactions demo
+ -->
<feature name="transactions-openjpa-demo">
<feature>jpa</feature>
<feature>jndi</feature>
View
@@ -0,0 +1,28 @@
+# Routing bundle
+In this bundle, we will bring together the services we set up in all our other bundles inside one Camel route. We will
+receive JMS messages, transform them into a `Flight` POJO and then persist those into the database using JPA.
+
+For coördinating the transactions with the JMS broker and the RDBMS, we will use Fuse ESB Enterprise's built-in JTA support
+(based Aries JTA).
+
+## Spring XML
+The Spring XML file at `src/main/java/resources/META-INF/spring/routing.xml' is where we glue everything together.
+
+First, we retrieve some of the services available in the OSGi Service Registry:
+
+* the global transaction manager provided by Fuse ESB Enterprise
+* the JPA EntityManagerFactory that we set up ourselves in the `database` module
+
+Afterwards, we define our two Camel components and link them up with the correct shared services:
+
+* the JMS component is defined with an XA-aware connection factory and a reference to the global transaction manager
+* the JPA component is defined with a reference to the JPA EntityManagerFactory
+
+The Spring XML file will also define the CamelContext itself, the Camel routes themselves are defined in a Java class.
+
+## TransactionalRouteBuilder
+The `src/main/java/org/fusesource/example/transactions/routes/TransactionalRouteBuilder.java` class defines the actual
+Camel routes. The code looks like a plain route from a JMS endpoint to a JPA endpoint, but there 2 little things to notice here:
+
+* the class extends SpringRouteBuilder, to allow for seamless integration between the route and Spring's support for transactions
+* the route contains the `transacted()` DSL method, which will setup the route for transactional support. This will also ensure that a suitable error handling mechanism is installed that supports transactions.
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.fusesource.example.transactions.routes;
+
+import java.util.Random;
+
+/**
+ * Helper class to generate random airport codes out of 50 of the busiest airports in the world.
+ */
+public class Airports {
+
+ private static final String[] AIRPORTS =
+ new String[] {"ATL", "PEK", "LHR", "ORD", "HND", "LAX", "CDG", "DFW", "FRA", "HKG",
+ "DEN", "CGK", "DXB", "AMS", "MAD", "BKK", "JFK", "SIN", "CAN", "LAS",
+ "PVG", "SFO", "PHX", "IAH", "CLT", "MIA", "MUC", "KUL", "FCO", "IST",
+ "SYD", "MCO", "ICN", "DEL", "BCN", "LGW", "EWR", "YYZ", "SHA", "MSP",
+ "SEA", "DTW", "PHL", "BOM", "GRU", "MNL", "CTU", "BOS", "SZX", "MEL"};
+
+ private static final Random RANDOM = new Random();
+
+ /**
+ * Generate a random airport code.
+ */
+ public static final String randomAirport() {
+ return AIRPORTS[RANDOM.nextInt(AIRPORTS.length)];
+ }
+
+}
@@ -1,32 +1,51 @@
+/**
+ * Copyright (c) Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.fusesource.example.transactions.routes;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
-import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spring.SpringRouteBuilder;
import org.fusesource.example.transactions.database.Flight;
-import java.util.Date;
import java.util.Random;
+import static org.fusesource.example.transactions.routes.Airports.randomAirport;
+
/**
- * Created with IntelliJ IDEA.
- * User: gert
- * Date: 27/11/12
- * Time: 12:17
- * To change this template use File | Settings | File Templates.
+ * Camel route builder defining our transactional route. Because we want to maximize the level of support Spring offers for transactions,
+ * we are extending SpringRouteBuilder instead of a plain RouteBuilder.
+ *
+ * The transacted() DSL keyword will configure the route with transaction support and add a specific transaction-aware error handler.
+ * It will lookup the transactional policy in the Spring XML file by default, but you can also specify one explicitly.
*/
public class TransactionalRouteBuilder extends SpringRouteBuilder {
@Override
public void configure() throws Exception {
- from("amq://Input.Orders?username=admin&password=admin")
- .transacted()
- .to("log:Orders?showAll=true")
- .process(new ConvertToJpaBeanProcessor())
- .to("jpa://org.fusesource.example.transactions.database.Flight");
+ from("amq://Input.Flights?username=admin&password=admin")
+ .transacted()
+ .log("Received JMS message ${body}")
+ .process(new ConvertToJpaBeanProcessor())
+ .log("Storing ${body} in the database")
+ .to("jpa://org.fusesource.example.transactions.database.Flight");
}
+ /*
+ * Just a simple Camel processor to transform a plain text message into a Flight object.
+ */
private class ConvertToJpaBeanProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
@@ -36,19 +55,10 @@ public void process(Exchange exchange) throws Exception {
Flight flight = new Flight();
flight.setNumber(number);
- flight.setDeparture("BRU");
- flight.setArrival("LON");
- flight.setDate(new Date());
-
- if (new Random().nextInt(3) == 2) {
- System.out.println("Uh oh, Murphy's Law in action - let's try to make this fail!");
-
- // setting the flight number to null - this will cause the INSERT to fail!
- flight.setNumber(null);
- }
+ flight.setDeparture(randomAirport());
+ flight.setArrival(randomAirport());
exchange.getOut().setBody(flight);
}
-
}
}
@@ -18,33 +18,51 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
- http://www.springframework.org/schema/osgi
- http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+ <!--
+ This sets up the Camel context - routes are defined in the Java Fluent API in the TransactionalRouteBuilder class
+ -->
<camelContext id="transactions.camel" xmlns="http://camel.apache.org/schema/spring">
<package>org.fusesource.example.transactions.routes</package>
</camelContext>
- <bean id="connectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
- <property name="brokerURL" value="tcp://localhost:61616" />
- </bean>
+ <!--
+ Aries JTA provides a transaction manager that implements org.springframework.transaction.PlatformTransactionManager.
+ We can use <osgi:reference/> to lookup this service from the OSGi Service Registry and start using it in this Spring file
+ -->
+ <osgi:reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
+
+ <!--
+ Setting up the amq:// components with an XA-aware connnection factory and a reference to the global transaction manager
+ reference we acquired.
+ -->
<bean id="amq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="connectionFactory" ref="connectionFactory" />
<property name="transactionManager" ref="transactionManager" />
</bean>
+ <bean id="connectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
+ <property name="brokerURL" value="tcp://localhost:61616" />
+ </bean>
+
+ <!--
+ Setting up the jta:// component with the entity manager factory that we set up in the 'database' bundle.
+ -->
<bean id="jpa" class="org.apache.camel.component.jpa.JpaComponent">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<osgi:reference id="entityManagerFactory" interface="javax.persistence.EntityManagerFactory" />
- <osgi:reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
-
+ <!--
+ Defines the transactional policy we want to use
+ -->
<bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="transactionManager" />
</bean>

0 comments on commit 297ab7a

Please sign in to comment.