Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 6
Not all vendors make this easy. An alternative, which works for almost any database, is to use Apache ActiveMQ for messaging and plug a storage strategy into the message broker. This is fairly easy to configure once you know the trick.
It's demonstrated in this article's shared-jms-db samples project. The application code (unit tests in this case) does not need to be aware that this pattern is in use, because
it is all enabled declaratively in Spring configuration.
A unit test in the sample called SynchronousMessageTriggerAndRollbackTests verifies that everything is working with synchronous message reception. The testReceiveMessageUpdateDatabase method receives two messages and uses them to insert two records in the database. When this method exits, the test framework
rolls back the transaction, so you can verify that the messages and the database updates are both rolled back, as shown in
Listing 3:
@AfterTransaction
public void checkPostConditions() {
assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS"));
List<String> list = getMessages();
assertEquals(2, list.size());
}
The most important features of the configuration are the ActiveMQ persistence strategy, linking the messaging system to the
same DataSource as the business data, and the flag on the Spring JmsTemplate used to receive the messages. Listing 4 shows how to configure the ActiveMQ persistence strategy:
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
depends-on="brokerService">
<property name="brokerURL" value="vm://localhost?async=false" />
</bean>
<bean id="brokerService" class="org.apache.activemq.broker.BrokerService" init-method="start"
destroy-method="stop">
...
<property name="persistenceAdapter">
<bean class="org.apache.activemq.store.jdbc.JDBCPersistenceAdapter">
<property name="dataSource">
<bean class="com.springsource.open.jms.JmsTransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="dataSource"/>
<property name="jmsTemplate" ref="jmsTemplate"/>
</bean>
</property>
<property name="createTablesOnStartup" value="true" />
</bean>
</property>
</bean>
Listing 5 shows the flag on the Spring JmsTemplate that is used to receive the messages:
JmsTemplate for transactional use
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
...
<!-- This is important... -->
<property name="sessionTransacted" value="true" />
</bean>
Without sessionTransacted=true, the JMS session transaction API calls will never be made and the message reception cannot be rolled back. The important
ingredients here are the embedded broker with a special async=false parameter and a wrapper for the DataSource that together ensure that ActiveMQ uses the same transactional JDBC Connection as Spring.
A shared database resource can sometimes be synthesized from existing separate resources, especially if they are all in the
same RDBMS platform. Enterprise-level database vendors all support the notion of synonyms (or the equivalent), where tables
in one schema (to use the Oracle terminology) are declared as synonyms in another. In that way data that is partitioned physically
in the platform can be addressed transactionally from the same Connection in a JDBC client. For example, the implementation of the shared-resource pattern with ActiveMQ in a real system (as opposed
to the sample) would usually involve creating synonyms for the messaging and business data.
javax.transaction from the Java docs for JTA and XAResource.