Статьи

Транзакции JMS и платформа SOA

Независимо от того, насколько тщательно вы планируете, неизбежно, что создаваемое вами программное обеспечение столкнется с ситуацией или цепочкой событий, которые вы не ожидали, и столкнется с каким-либо типом сбоя. (Поверьте мне. Я занимаюсь тестированием программного обеспечения и большую часть своего бодрствования провожу, либо вызывая сбои программного обеспечения, либо отлаживая их.) Поскольку избежать всех сбоев невозможно, возможность восстановления после сбоев является важной частью любого программного обеспечения. Системный дизайн.

Классический тип провала предполагает перемещение денег между банковскими счетами. Это двухэтапный набор действий. Во-первых, деньги снимаются с одного счета, а во-вторых, деньги зачисляются на второй счет. Это звучит просто, верно? Хорошо, предположим, что система сталкивается с ошибкой после того, как она сняла деньги с первого счета, но до того, как у нее была возможность внести депозит на второй счет? Что происходит с деньгами? Как вы можете избежать потери этих денег? Ответ заключается в том, что вы заключаете эти два действия в транзакцию.

Транзакции и ACID Свойства

Что именно является «транзакцией»? Как и многие вещи в программном обеспечении, это модное слово, которое злоупотребляют. Но помимо этого, транзакция — это логическая группировка действий в одно большее действие, где это большее действие выполняется или не выполняется на основе полной или нулевой оценки результатов отдельных действий
[1] . В случае примера перевода денег, который мы только что упомянули, объединение двух переводов в транзакцию гарантирует, что в случае неудачного перевода ваши деньги не будут потеряны, и оба счета вернутся к своему первоначальному состоянию.

Транзакции часто описываются с точки зрения наличия набора свойств «ACID»
[2] :

  • Атомарность. Когда мы говорим о желании транзакции быть «атомарной», мы имеем в виду, что мы хотим, чтобы транзакция, которая будет состоять из набора отдельных действий, рассматривалась как единая атомарная единица. Если какое-либо из этих действий завершается неудачно, например, в одном из перечислений перевод банковского счета, вся транзакция отменяется. (Используемый для этого термин «откат».) Транзакция представляет собой конструкцию «все или ничего». Все действия, приложенные к транзакции, должны быть успешно завершены до завершения самой транзакции (или «совершено»).
  • Согласованность — транзакции не оставляют данные, с которыми они работают, в частично завершенном состоянии. Если транзакция откатывается, соответствующие данные или база данных возвращаются в (согласованное) состояние, в котором они находились до начала транзакции.
  • Изоляция. В контексте транзакции «изоляция» означает, что любые условия или состояние, создаваемые транзакцией, не видны какой-либо другой транзакции. Например, если перевод нашего банковского счета был выполнен в транзакции, то во время выполнения любые другие транзакции или операции не смогут увидеть какие-либо результаты транзакции, пока вся транзакция не будет завершена.
  • Долговечность. Одна из опасностей примера перевода банковского счета, если его попытаться совершить за пределами транзакции, заключается в том, что если что-то пойдет не так, данные (или, что еще хуже, деньги!) Будут потеряны. Поскольку транзакция гарантирует согласованность данных, эти данные надежны. Если транзакция откатывается, исходные данные восстанавливаются.

Транзакции и JBossESB платформы SOA

Хорошо, это все интересно, но звучит как что-то лучше подходящее для баз данных, чем сервис-ориентированная архитектура Как транзакционная модель применяется к JBossESB в платформе SOA, где все является либо сообщением, либо службой?

Вот как: некоторые транспорты, поддерживаемые платформой (InVM
[3] и JMS), поддерживают транзакционную доставку сообщений. Фактическая доставка этого сообщения не произойдет, пока транзакция не будет зафиксирована. Как вы можете вызвать откат транзакции в конвейере действий? Настраивая приложение и его службы для включения транзакции, а затем вызывая исключение RuntimeException в конвейере действий. Лучший способ объяснить и проиллюстрировать, как это происходит, с помощью одной из примеров программ «быстрого запуска» платформы SOA. Позволять’
смотри.

Быстрый запуск

Одной из замечательных особенностей платформы SOA является ее обширный и постоянно растущий набор программ быстрого запуска. Эти программы иллюстрируют различные функции, поддерживаемые платформой, и служат отличным ресурсом для написания ваших собственных приложений. В нашем примере мы рассмотрим метко запускаемый файл с именем jms_transacted.

Этот краткий обзор иллюстрирует использование JMS-транспорта с JBossESB в платформе SOA для обработки транзакций. Быстрый старт также показывает, как можно выполнить доставку сообщений с помощью JMS-транспорта. Прежде чем мы рассмотрим код быстрого запуска, файлы конфигурации и фактический вывод, важно отметить использование быстрого запуска JCA (Java Connector Architecture)
[4]., JCA предоставляет стандартизированный способ подключения к провайдерам JMS. Для JBossESB в платформе SOA провайдеры jms-jca предоставляют поддержку транзакций для конвейера действий, заключая действия в транзакцию JTA
[5] . Эти транзакции обеспечивают обработку сообщений в рамках транзакции. Если что-то идет не так в транзакции, сообщения помещаются обратно в очереди JMS для последующей обработки. Мы увидим это в действии, когда запустим быстрый старт.

Последовательность действий, которые выполняет быстрый запуск:

  • Как и во многих быстрых запусках платформы SOA, все начинается с того, что сообщение JMS направляется в службу для запуска конвейера действий.
  • При первом запуске конвейера строка вставляется в базу данных быстрого старта HSQLDB. Обратите внимание, что, хотя платформа SOA поддерживает несколько баз данных, таких как MySQL, Oracle, PostgreSQL и другие, в производственных средах она включает в себя полнофункциональную базу данных HSQLDB для демонстрационных целей.
  • Затем вызывается класс org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction, чтобы вызвать исключение. Этот класс действий настроен на повышение исключения (5) раз, чтобы вызвать откат транзакции, которая включает записи в базу данных. Каждый раз, когда возникает исключение, адаптер JCA откатывает транзакцию. Исключение также распространяется на адаптер JCA и записывает сообщения об ошибках в журнал.
  • После достижения настроенного предела (5) откатов класс org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction завершается, и транзакция фиксируется с помощью org / jboss / soa / esb / JBossESB платформы SOA. класс common / JBossESBTransactionService, так что чистый результат — только одна строка, записанная в базу данных.

Теперь мы подробно рассмотрим, как настроена эта транзакция, и что происходит при быстром запуске.

Быстрый старт — подробно

Транзакция в быстром запуске включает как обмен сообщениями JMS, так и запись в базу данных. Давайте начнем с изучения подключения к базе данных HSQLDB, которую использует быстрый запуск. Во-первых, нам нужно создать определение источника данных для подключения к базе данных.

В файле: quickstart-ds.xml:

<datasources>
<local-tx-datasource>
<jndi-name>JmsTransactedDB</jndi-name>
<connection-url>jdbc:hsqldb:hsql://localhost:1706</connection-url>
<driver-class>org.hsqldb.jdbcDriver</driver-class>
<user-name>sa</user-name>
<password></password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
<idle-timeout-minutes>0</idle-timeout-minutes>
<depends>jboss:service=JmsTransactedDB</depends>
<prepared-statement-cache-size>32</prepared-statement-cache-size>
</local-tx-datasource>

<mbean code="org.jboss.internal.soa.esb.dependencies.HypersonicDatabase"
name="jboss:service=JmsTransactedDB">
<attribute name="Port">1706</attribute>
<attribute name="BindAddress">localhost</attribute>
<attribute name="Database">JmsTransactedDB</attribute>
<attribute name="Silent">true</attribute>
<attribute name="Trace">false</attribute>
<attribute name="No_system_exit">true</attribute>
<attribute name="DataDir">${jboss.server.data.dir}</attribute>
</mbean>
</datasources>

Некоторые пункты, чтобы отметить здесь:

  • Строка 3 — Мы определяем источник данных local-tx-data, так как нам нужно соединение JCA с поддержкой транзакций [6] .
  • Строка 4 — Мы используем это имя JNDI для ссылки на источник данных.
  • Строки 5-13 — класс драйвера БД, соединение, URL, имя пользователя / пароль и т. Д. Для соединения с базой данных
  • Строка 12 — интерфейс MBean для запуска Hypersonic в той же виртуальной машине с JBoss — обратите внимание, что имя службы и имя JNDI, как мы будем ссылаться на них позже.

Хорошо, у нас есть источник данных, который мы можем использовать, чтобы попасть в базу данных HSQLDB и получить нас там в режиме, который поддерживает транзакции. Что дальше? Мы должны убедиться, что наша база данных действительно существует, прежде чем мы попытаемся ее использовать! К счастью, у платформы SOA есть класс, который может инициализировать нашу базу данных при развертывании приложения .esb.

В файле: jbossesb-service.xml

<?xml version="1.0" encoding="UTF-8"?>

<server>
<mbean code="org.jboss.internal.soa.esb.dependencies.DatabaseInitializer"
name="jboss.esb:service=JmsTransactedDatabaseInitializer">
<attribute name="Datasource">java:/JmsTransactedDB</attribute>
<attribute name="ExistsSql">select * from jms_transacted_table</attribute>
<attribute name="SqlFiles">
hsqldb/create.sql
</attribute>
<depends>jboss.jca:name=JmsTransactedDB,service=DataSourceBinding</depends>
</mbean>
</server>

В этом файле (3) есть интересные вещи:

  • Строка 4 — Этот MBean создает базу данных при запуске. Как это связано с базой данных? Смотрите строку 6.
  • Строка 6 — вот ссылка на наш источник данных. Обратите внимание на имя JNDI, которое мы наблюдали в файле определения источника данных. Как DatabaseInitializer узнает, как выглядит схема базы данных? Смотрите строку 9.
  • Строка 9 — вот ссылка на файл быстрого запуска src / hsqldb / create.sql. Этот файл создает очень простую таблицу в нашей базе данных для быстрого запуска:

В файле: src / hsqldb / create.sql:

create table jms_transacted_table
(
unique_id INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY,
data_column VARCHAR(255) NOT NULL
);

Что еще нужно базе данных быстрого старта? Служба, которая создает базу данных (JmsTransactedDatabaseInitializer), должна быть развернута. Это обрабатывается в файле deploy.xml (как и очереди сообщений JBoss, которые будет использовать быстрый запуск):

<jbossesb-deployment>
<depends>jboss.esb.quickstart.destination:service=Queue,name=quickstart_jms_transacted_Request_esb</depends>
<depends>jboss.esb.quickstart.destination:service=Queue,name=quickstart_jms_transacted_Request_gw</depends>
<depends>jboss.esb:service=JmsTransactedDatabaseInitializer</depends>
</jbossesb-deployment>

И, наконец, файлы jbossesb-service.xml quickstart-ds.xml должны быть включены в файл архива приложения быстрого запуска .esb. Это обрабатывается свойством «Additional.deploys» в файле build.xml быстрого запуска и файлом ../conf/base-build.xml, который используется всеми быстрыми запусками.

cat -n build.xml | grep quick
<property name="additional.deploys" value="jbossesb-service.xml quickstart-ds.xml" />

Хорошо, это заботится о базе данных. Теперь давайте посмотрим, как мы настраиваем быстрый запуск, чтобы использовать преимущества поддержки транзакций в JBossESB в платформе SOA. Как всегда, начинать нужно с файла jboss-esb.xml.

В файле: jboss-esb.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.0.1.xsd" parameterReloadSecs="5">

<providers>
<jms-jca-provider name="JBossMessaging" connection-factory="XAConnectionFactory">

<jms-bus busid="quickstartGwChannel">
<jms-message-filter
dest-type="QUEUE"
dest-name="queue/quickstart_jms_transacted_Request_gw"
transacted="true"
/>
</jms-bus>
<jms-bus busid="quickstartEsbChannel">
<jms-message-filter
dest-type="QUEUE"
dest-name="queue/quickstart_jms_transacted_Request_esb"
transacted="true"
/>
</jms-bus>
<activation-config>
<!-- The maximum number of times a message is redelivered before it is sent to the DLQ -->
<property name="dLQMaxResent" value="5"/>
</activation-config>

</jms-jca-provider>
</providers>

<services>
<service
category="JMSSecuredESB"
name="SimpleListener"
description="JMS Secured quickstart sample">
<listeners>
<jms-listener name="JMS-Gateway"
busidref="quickstartGwChannel"
is-gateway="true"/>
<jms-listener name="jmssecured"
busidref="quickstartEsbChannel"/>
</listeners>
<actions mep="OneWay">

<action name="printMessage" class="org.jboss.soa.esb.actions.SystemPrintln">
<property name="message" value="JMS Transacted Quickstart entered. Message body"/>
<property name="printfull" value="false"/>
</action>

<action name="insertDBAction" class="org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction">
<property name="datasource-name" value="java:JmsTransactedDB"/>
<property name="db-insert-sql" value="insert into jms_transacted_table(data_column) values(?)"/>
</action>

<!--
Will throw an Exception for the configured number of rollbacks. This should trigger the transaction to be
rolledback and the message placed back onto the JMS queue.
-->
<action name="throwExceptionAction" class="org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction">
<property name="rollbacks" value="5"/> <!-- if greater than dLQMaxResent then message goes to the JMS DLQ -->
</action>

<action name="printMessageDone" class="org.jboss.soa.esb.actions.SystemPrintln">
<property name="message" value="JMS Transacted Quickstart processed successfully. Message body"/>
<property name="printfull" value="false"/>
</action>

<!-- The next action is for Continuous Integration testing -->
<action name="testStore" class="org.jboss.soa.esb.actions.StoreMessageToFile"/>
</actions>
</service>
</services>

</jbossesb>

  • Line 2 — Before we go any further, we should look at the XSD (XML schema definition) for jboss-esb.xml files. The schema definition is available here: http://anonsvn.jboss.org/repos/labs/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.0.1.xsd. If you’re going to be working with the SOA Platform, it’s a good idea to become familiar with this XSD as all the elements that you work with in jboss-esb.xml are defined there. The two main elements are of course, providers and services. Let’s look at the providers first. Two types of providers are supported. Schedule providers define schedule driven listeners that pull messages from queues at (you guessed it) scheduled time periods. In contrast, bus providers such as the jms-jca-providers that we’re using in this quickstart, are associated with listeners that have messages pushed to them. [7]
  • Line 5 — Here’s the start of our jms-jca-provider. Note that the XAConnectionFactory provides support for distributed transactions
  • Lines 7-13 — This is a reference to the channel and JMS queue that will be used when a JMS message that is sent to a JMS gateway to initiate the quickstart. Let’s stop for a second and talk about the forms in which data is moved onto and across the JBossESB in the SOA Platform. One of the primary functions performed by JBossESB is the routing of messages between services. (I said it before, and I’ll say it again; remember that, on an ESB, everything is either a message or a service.) The messages that JBoss ESB handles conform to org.jboss.soa.esb.message.Message. Service endpoints that can process messages in this format are described as being «ESB-aware.» This is all well and good for services and applications that were designed and written with this message format in mind, but what about other services, including legacy applications, that communicate through other non-ESB aware data formats? JBossESB handles connecting services that communicate in non-ESB aware message formats through its gateway listeners. These listeners route incoming messages from outside the ESB to ESB services. The ESB’s set of gateway listeners (generally referred to as «gateways») support a variety of message formats such as files, FTP, and JMS. These gateways accept messages in a non-ESB aware format onto the ESB and then convert them (actually, they wrap the message payload) into ESB-aware messages before sending them to their service’s action pipeline.
  • Line 11 — Remember, we want the service to process messages within a transaction.
  • Lines 14-20 — Gateways are intended to enable the JBossESB to communicate to external data sources. The gateways themselves don’t move data across the ESB. Accordingly, for each Gateway channel that we define, we have to define a corresponding ESB-aware channel to be used to route messages within the ESB.
  • Line 21 — The activation-config defines how we link to the JCA. In the case of this quickstart, the interesting property is: dLQMaxResent. As the comment indicates, the “DLQMaxResent” controls how many times the JCA adapter resends the message before it is sent to the dead letter queue. This is a JBossESB service that handles messages for transports when the messages cannot be delivered. (The JMS transport actually has its own dead letter queue as JBoss Messaging is shipped with a set of pre-configured destinations defined in destinations-service.xml including: <mbean code=»org.jboss.jms.server.destination.Queue» name=»jboss.messaging.destination:service=Queue,name=DLQ») In this quickstart, the JCA adapter will try to send the message 5 times.

После того, как наши провайдеры определены, мы можем определить наши услуги.

Служба — это то, где все происходит. Каждое определение сервиса включает в себя набор слушателей и последовательный набор действий, называемых «конвейером действий». Слушатели «прослушивают» входящие сообщения для службы и направляют эти сообщения в конвейер действий.

После того, как имя и категория службы определены в строках 31 и 32, мы определяем слушателей службы:

  • Строки 35-37. Вот определение слушателя шлюза JMS. Обратите внимание, как он ссылается на идентификатор шины канала, который мы определили ранее в определении провайдера. Также обратите внимание, что для свойства is-gateway установлено значение true, чтобы указать, что этот прослушиватель является шлюзом.
  • Строки 38-39. И вот слушатель, поддерживающий ESB, который соответствует слушателю шлюза.

Так много для слушателей, теперь давайте посмотрим на действия.

  • Строка 41 — это начало определений последовательности действий, которые мы назвали конвейером действий. Шаблон обмена сообщениями (или «mep») указывает, что сообщения не обмениваются в шаблоне запроса / ответа, а скорее отправляются только в одном направлении. (Напротив, веб-сервисы, предоставляемые на ESB, будут следовать синхронному шаблону запрос / ответ.)
  • Строки 43-46 — это действие вызывает простейшее из множества полезных готовых действий JBossESB (http://soa.dzone.com/articles/works-great-right-out-box) для написания некоторых сообщений. в журнал сервера.
  • Lines 48-51 — Here’s where things start to get interesting. This custom action writes a row to our database table. Remember the «java:JmsTransactedDBdatasource» that we defined? Here’s where gets used.
  • Line 57 — Remember how we talked about how to cause a rollback of a transaction in the action pipeline to occur? We configured the .esb to include a transaction, and now we raise a RuntimeException in the action pipeline. The action defined here raises an exception of a type that we define in the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction class. The ActionProcessingPipeline will catch the exception and pass it up the stack to its caller, the JMS/JCA adapter. The Adapter then causes the transaction to rollback and the message is redelivered.
  • Line 58 — When we defined the jms-jca-provider’s activation-config, we specified a dLQMaxResent value of 5 to indicate that if the transaction was rolled back more than 5 times, the message processed in the transaction should be sent to the JMS dead letter queue. In line 58, we also set the value of the rollbacks property equal to 5, to ensure that the transaction can complete after 5 rollbacks, and the message is not sent to the dead letter queue service. As the comment helpfully explains, setting the rollbacks property to be greater than dLQMaxResent will send the message to the dead letter queue service.
  • Lines 61-64 This action is only reached after the rollbacks have been performed and the transaction is able to complete. The action writes the message body to the server log.

Йоги Берра однажды сказал, что «в теории нет разницы между теорией и практикой. На практике есть».
[8] ОК. Достаточно теории, давайте запустим быстрый старт и посмотрим, что произойдет.

После запуска сервера SOA-P

быстрый запуск развертывается с помощью этой команды: ant deploy
Когда быстрый запуск развернут, в server.log следующее:

2010-04-07 09:26:22,117 INFO  [org.jboss.resource.connectionmanager.ConnectionFactoryBindingService] (HDScanner) Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=JmsTransactedDB' to JNDI name 'java:JmsTransactedDB'
2010-04-07 09:26:22,157 INFO [org.jboss.internal.soa.esb.dependencies.DatabaseInitializer] (HDScanner) Initializing java:/JmsTransactedDB from listed sql files
2010-04-07 09:26:22,326 INFO [org.jboss.soa.esb.listeners.deployers.mc.EsbDeployment] (HDScanner) Starting ESB Deployment 'Quickstart_JMS_Transacted.esb'

Обратите внимание, как база данных JmsTransactedDB инициализируется классом org.jboss.internal.soa.esb.dependencies.DatabaseInitializer при развертывании быстрого запуска.

Далее мы запускаем быстрый запуск с помощью этой команды: ant runtest

Когда выполняется цель ant runtest, запускается следующая цепная реакция:

org.jboss.soa.esb.samples.quickstart.jmstransacted.test.SendJMSMessage класс отправляет (не -ESB знает) JMS-сообщение в очередь quickstart_jms_transacted_Request_gw. Сообщение направляется через соответствующую очередь ESB с поддержкой ESB (quickstart_jms_transacted_Request_esb) в службу SimpleListener, и инициируется конвейер действий.

При первом запуске конвейера действий класс org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction вставляет строку в базу данных через источник данных HSQLDB. Это приводит к тому, что в server.log записываются следующие сообщения:

Затем выдается исключение времени выполнения org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction.

Поскольку это исключение выдается из конвейера действий, адаптер JMS / JCA откатывает вмещающую транзакцию. Исключение распространяется на адаптер JMS / JCA, поскольку исключение передается по стеку от конвейера действий к вызывающей стороне (которая является адаптером). Исключение перехватывается org.jboss.soa.esb.listeners.message.ActionProcessingPipeline JBossESB и распространяется вверх по стеку к адаптеру JMS / JCA (который является вызывающим объектом конвейера действий). Адаптер вызывает откат транзакции. Это приводит к удалению строки из базы данных и доставке исходного сообщения. Помните, что мы говорили о том, что транзакция является событием «все или ничего»?

Затем последовательность транзакций / откатов повторяется до тех пор, пока не будет выполнено настроенное количество откатов. Вы можете увидеть счетчик (поддерживаемый классом org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction) количества попыток выполнения действия.

Действие printMessage печатает сообщение в журнал:

2010-04-07 09:26:35,340 INFO  [STDOUT] (WorkManager(2)-9) JMS Transacted Quickstart entered. Message body:
2010-04-07 09:26:35,340 INFO [STDOUT] (WorkManager(2)-9) [Hello Transacted JMS World]].

А действие insertDBAction записывает строку в базу данных — обратите внимание на значение счетчика [1]:

2010-04-07 09:26:35,415 INFO  [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction] (WorkManager(2)-9) Successfully inserted [Hello Transacted JMS World] counter[1]] into jms_transacted_table

Но ждать! Тогда возникает наше исключение:

2010-04-07 09:26:35,488 ERROR [org.jboss.resource.adapter.jms.inflow.JmsServerSession] (WorkManager(2)-9) Unexpected error delivering message delegator->JBossMessage[5204569273565185]:PERSISTENT, deliveryId=0
java.lang.IllegalStateException: [Throwing Exception to trigger a transaction rollback]
at org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction.process(ThrowExceptionAction.java:75)

Адаптер JMS / JCA откатывает транзакцию, и сообщение доставляется снова, и в базу данных записывается другая строка. На этот раз счетчик увеличивается до [2], и исключение снова вызывается.

2010-04-07 09:26:36,435 INFO  [STDOUT] (WorkManager(2)-10) JMS Transacted Quickstart entered. Message body:
2010-04-07 09:26:36,435 INFO [STDOUT] (WorkManager(2)-10) [Hello Transacted JMS World]].
2010-04-07 09:26:36,438 INFO [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction] (WorkManager(2)-10) Successfully inserted [Hello Transacted JMS World] counter[2]] into jms_transacted_table
2010-04-07 09:26:36,442 ERROR [org.jboss.resource.adapter.jms.inflow.JmsServerSession] (WorkManager(2)-10) Unexpected error delivering message delegator->JBossMessage[5204569273565185]:PERSISTENT, deliveryId=1
java.lang.IllegalStateException: [Throwing Exception to trigger a transaction rollback]
at org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction.process(ThrowExceptionAction.java:75)

Когда номер счетчика отката достигнут, конвейер действий завершается и транзакция фиксируется, так что строка наконец записывается в базу данных. Следующие сообщения записываются в servler.log — эта последовательность повторяется (5) раз, пока счетчик не будет передан и строка фактически не записана в базу данных:

2010-04-07 09:26:40,489 INFO  [STDOUT] (WorkManager(2)-14) JMS Transacted Quickstart entered. Message body:
2010-04-07 09:26:40,489 INFO [STDOUT] (WorkManager(2)-14) [Hello Transacted JMS World]].
2010-04-07 09:26:40,490 INFO [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction] (WorkManager(2)-14) Successfully inserted [Hello Transacted JMS World] counter[6]] into jms_transacted_table
2010-04-07 09:26:40,490 INFO [STDOUT] (WorkManager(2)-14) JMS Transacted Quickstart processed successfully. Message body:
2010-04-07 09:26:40,490 INFO [STDOUT] (WorkManager(2)-14) [Hello Transacted JMS World]].

Теперь, как я уже упоминал в начале этого поста, я работаю инженером по программному обеспечению. Один из основных принципов программного обеспечения QE — никогда не доверять ничему. Давайте подробнее рассмотрим, что происходит, когда запускается быстрый старт.

Журналы сервера являются золотыми приисками для сбора отладочной информации. Давайте перезапустим сервер, но на этот раз установите уровень ведения журнала сервера на DEBUG:

./run.sh -Djboss.server.log.threshold=DEBUG

А затем снова запустите быстрый запуск. На этот раз server.log будет гораздо более многословным, но если вы посмотрите внимательно, вы увидите счетчик отката в действии:

grep  nr-of-rollbacks server.log
2010-04-07 23:14:19,776 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-11) rollbackCounter [0], nr-of-rollbacks [5]
2010-04-07 23:14:20,804 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-12) rollbackCounter [1], nr-of-rollbacks [5]
2010-04-07 23:14:21,819 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-13) rollbackCounter [2], nr-of-rollbacks [5]
2010-04-07 23:14:22,833 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-14) rollbackCounter [3], nr-of-rollbacks [5]
2010-04-07 23:14:23,848 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-15) rollbackCounter [4], nr-of-rollbacks [5]
2010-04-07 23:14:24,858 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-16) rollbackCounter [5], nr-of-rollbacks [5]

Прежде чем мы продолжим, давайте рассмотрим еще одну вещь и убедимся, что только одна строка действительно записана в базу данных. Теперь быстрый запуск очень аккуратен и всегда убирает за собой, удаляя строки из базы данных. Давайте отключим эту очистку, удалив эту цель из файла build.xml быстрого запуска:

cat build.xml | grep truncate

And, let’s start the server again, but with one additional command line option: sh ./run.sh -Djava.awt.headless=false

Why «headless=false?» After we run the quickstart again, the row that was written to the database should still be there. We’ll look at it with the HSQL DB Manager service. This service requires the server to not be started in its default «headless» mode, as the service opens up a Java UI application. We invoke the DB Manager from the JMX console — see screenshot — the service tag is: database=jbossesb,service=Hypersonic.

Once the DM Manager application UI launches, we just connect to the «JmsTransactedDB» database — see screenshot — and execute a query to return all rows in the «jms_transacted_table» — see screenshot.

 

And, there is our one record!


Before we move on, it’s worthwhile to note what we did NOT have to do to execute a transaction with the SOA Platform.

We did not have to write transaction specific code. The JBossESB in the Platform did the dirty work for us. All we had to do is to define the .esb application’s configuration in its jboss-esb.xml file to include the transaction-relevant properties and reference a JMS/JCA adapter and the Platform did the rest. This is what makes middleware so useful. You could write the code to handle transactions all on your own, but wouldn’t you rather concentrate on solving your own business problems instead of build infrastucture plumbing? [9]

Closing Thoughts

Ценность, которую использование транзакций добавляет приложению, довольно очевидна (просто помните свой банковский счет). Прибыль промежуточного программного обеспечения при реализации транзакций также должна быть очевидной. Вы можете написать свой собственный код для управления атомарностью, согласованностью, изоляцией и долговечностью, но это будет нелегкой задачей, и это также отнимет у вас много времени, если вы сможете сконцентрироваться на решении собственных проблем бизнес-логики. Поддержка платформой SOA транзакций через JBossESB позволяет вам обеспечить более высокий уровень надежности и долговечности для ваших сервисных приложений.

Рекомендации

  1.  http://qconlondon.com/london-2010/file?path=/qcon-london-2010/slides/MarkLittle_TransactionsOverUsedOrJustMisunderstood.pdf
  2.  Little, Mark, Maron, Jon, Pavlik, Greg. Transaction Processing: Design and Implementation, Upper Saddle RIver, new Jersey: Prentice Hall/Hewlett-Packard Professional Books, 2004.
  3.  http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/5.0.0/html-single/Programmers_Guide/index.html#sect-SOA_ESB_Programmers_Guide-What_is_a_Service-InVM_Transport
  4.  http://java.sun.com/j2ee/connector/
  5.  http://java.sun.com/javaee/technologies/jta/index.jsp
  6.  http://www.jboss.org/file-access/default/members/jbossas/freezone/docs/Server_Configuration_Guide/4/html/Connectors_on_JBoss-Configuring_JDBC_DataSources.html
  7.  http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/5.0.0/html-single/Programmers_Guide/index.html#sect-SOA_ESB_Programmers_Guide-Configuration-Providers
  8.  http://en.wikiquote.org/wiki/Yogi_Berra
  9.  http://magazine.redhat.com/2008/03/11/what-is-middleware-in-plain-english-please

Благодарности

Как всегда, я хочу поблагодарить команду и сообщество JBoss SOA Platform (особенно Кевина Коннера за его своевременную рецензию на этот пост в блоге).