Claus,

I'm still struggling with this so I've put together a quick sample project
that shows the problem. It consists of an OSGI component that runs under a
transaction and posts two JMS messages (one with Camel and one with JMS
APIs) then rolls back the transactions. I would hope to see both messages
not be delivered but instead what I see if the one sent via camel being
delivered while the other one is rolled back. I'm sure I'm probably doing
something wrong but I can't figure it out.

Is there a place I can post my sample project where someone might be able
to give it a quick look?

Thanks,
Chris

On Sat, Apr 7, 2012 at 3:19 AM, Claus Ibsen <claus.ib...@gmail.com> wrote:

> On Thu, Apr 5, 2012 at 5:57 PM, Chris Geer <ch...@cxtsoftware.com> wrote:
> > Claus,
> >
> > I realize that but I can't explain what I'm seeing. Here is an additional
> > piece of info, here is debug log for the sending of the message. As you
> can
> > see, the transaction fields are all null but I don't know if that is
> normal
> > or a symptom of the problem.
> >
> > 08:51:22,906 | DEBUG | erations/address | JmsConfiguration
> > | 169 - org.apache.camel.camel-jms - 2.9.2.SNAPSHOT | Sending JMS message
> > to: topic://event-notifications with message: ActiveMQBytesMessage
> > {commandId = 0, responseRequired = false, messageId = null,
> > originalDestination = null, originalTransactionId = null, producerId =
> > null, destination = null, transactionId = null, expiration = 0,
> timestamp =
> > 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0, correlationId =
> null,
> > replyTo = null, persistent = true, type = null, priority = 0, groupID =
> > null, groupSequence = 0, targetConsumerId = null, compressed = false,
> > userID = null, content = null, marshalledProperties = null,
> dataStructure =
> > null, redeliveryCounter = 0, size = 0, properties = {EntityType=Address,
> > breadcrumbId=ID-CXTMBP-Chris-local-62052-1333577461603-22-3,
> > EventType=EntityCreated, ClientID=0}, readOnlyProperties = false,
> > readOnlyBody = false, droppable = false} ActiveMQBytesMessage{ bytesOut =
> > org.apache.activemq.util.ByteArrayOutputStream@51762faf, dataOut =
> > java.io.DataOutputStream@2634b3f1, dataIn = null }
> >
>
> I would only suspect transaction ids being populated in the AMQ
> message if the message originated from the AMQ broker. Creating a new
> message to be send would most likely not populate TX ids and whatnot.
> But the work is still carried out under the TX manager. (if TX is
> properly configured and working - yeah thats the hard part).
>
> > Here is more of the stack trace that shows the transaction being
> committed
> > for some reason.
> >
> > 08:51:22,888 | DEBUG | erations/address | TransactionErrorHandler
> >  | 166 - org.apache.camel.camel-core - 2.9.2.SNAPSHOT | Transaction begin
> > (0x1f2198ab) redelivered(unknown) for (MessageId:
> > ID-CXTMBP-Chris-local-62052-1333577461603-22-3 on ExchangeId:
> > ID-CXTMBP-Chris-local-62052-1333577461603-22-4))
> > 08:51:22,888 | DEBUG | erations/address | JtaTransactionManager
> >  | 139 - org.springframework.transaction - 3.0.6.RELEASE | Participating
> in
> > existing transaction
> > 08:51:22,906 | DEBUG | erations/address | JmsConfiguration
> > | 169 - org.apache.camel.camel-jms - 2.9.2.SNAPSHOT | Sending JMS message
> > to: topic://event-notifications with message: ActiveMQBytesMessage
> > {commandId = 0, responseRequired = false, messageId = null,
> > originalDestination = null, originalTransactionId = null, producerId =
> > null, destination = null, transactionId = null, expiration = 0,
> timestamp =
> > 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0, correlationId =
> null,
> > replyTo = null, persistent = true, type = null, priority = 0, groupID =
> > null, groupSequence = 0, targetConsumerId = null, compressed = false,
> > userID = null, content = null, marshalledProperties = null,
> dataStructure =
> > null, redeliveryCounter = 0, size = 0, properties = {EntityType=Address,
> > breadcrumbId=ID-CXTMBP-Chris-local-62052-1333577461603-22-3,
> > EventType=EntityCreated, ClientID=0}, readOnlyProperties = false,
> > readOnlyBody = false, droppable = false} ActiveMQBytesMessage{ bytesOut =
> > org.apache.activemq.util.ByteArrayOutputStream@51762faf, dataOut =
> > java.io.DataOutputStream@2634b3f1, dataIn = null }
> > 08:51:22,907 | DEBUG | erations/address | JtaTransactionManager
> >  | 139 - org.springframework.transaction - 3.0.6.RELEASE | Registering
> > after-completion synchronization with existing JTA transaction
> > 08:51:22,907 | DEBUG | erations/address | TransactionErrorHandler
> >  | 166 - org.apache.camel.camel-core - 2.9.2.SNAPSHOT | Transaction
> commit
> > (0x1f2198ab) redelivered(unknown) for (MessageId:
> > ID-CXTMBP-Chris-local-62052-1333577461603-22-3 on ExchangeId:
> > ID-CXTMBP-Chris-local-62052-1333577461603-22-4))
> >
>
> That last debug logging is just Camel saying that the TX completed
> successfully (in that leg). Its up to the TX manager when actually to
> commit the TX. If a TX was started outside, then the commit is
> executed at that point.
>
> So this is normal.
>
> > On Thu, Apr 5, 2012 at 8:19 AM, Claus Ibsen <claus.ib...@gmail.com>
> wrote:
> >
> >> On Thu, Apr 5, 2012 at 4:59 PM, Chris Geer <ch...@cxtsoftware.com>
> wrote:
> >> > Christian,
> >> >
> >> > I have that book and that is what I used for a lot of my reference. In
> >> > fact, they only major difference between his source and mine is he is
> >> using
> >> > Atomikos as the transaction manager and I'm using aries. I am
> referencing
> >> > an existing PlatformTransactionManager instead of creating a
> >> > JtaTransactionManager but PlatformTransactionManager implements
> >> > JtaTransactionManager so it should be ok.
> >> >
> >> > As for the datasource, I'm actually publishing it from another OSGI
> >> > component as a service so it can be reused. I'm creating it in code
> right
> >> > now as defined below.
> >> >
> >> >        BasicManagedDataSource ds = new BasicManagedDataSource();
> >> >
> >> >        if(xaDataSourceClass != null && !xaDataSourceClass.isEmpty()) {
> >> >            try {
> >> >                XADataSource dsi =
> >> > (XADataSource)Class.forName(xaDataSourceClass).newInstance();
> >> >                Method setUrl = dsi.getClass().getMethod("setUrl", new
> >> > Class[] {String.class});
> >> >                setUrl.invoke(dsi, (String) config.get(CONNSTR_KEY));
> >> >                ds.setXADataSource(xaDataSourceClass);
> >> >                ds.setXaDataSourceInstance(dsi);
> >> >            } catch (IllegalArgumentException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Couldn't create instance", ex);
> >> >            } catch (InvocationTargetException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Couldn't create instance", ex);
> >> >            } catch (NoSuchMethodException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Couldn't create instance", ex);
> >> >            } catch (SecurityException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Couldn't create instance", ex);
> >> >            } catch (InstantiationException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Couldn't create instance", ex);
> >> >            } catch (IllegalAccessException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Couldn't create instance", ex);
> >> >            } catch (ClassNotFoundException ex) {
> >> >                throw new ConfigurationException("xaDataSourceClass",
> >> > "Class not found", ex);
> >> >            }
> >> >        } else {
> >> >            ds.setDriverClassName((String) config.get(DRIVER_KEY));
> >> >            ds.setUrl((String) config.get(CONNSTR_KEY));
> >> >        }
> >> >
> >> >        BundleContext context =
> >> >
> >>
> FrameworkUtil.getBundle(BedrockConnectionPoolFactory.class).getBundleContext();
> >> >
> >> >        ds.setTransactionManager(transMgr);
> >> >
> >> >        Hashtable<String, String> sp = new Hashtable<String, String>();
> >> >        sp.put(DSNAME_KEY, (String) config.get(DSNAME_KEY));
> >> >        ServiceRegistration reg =
> >> > context.registerService("javax.sql.XADataSource",
> >> > ds.getXaDataSourceInstance(), sp);
> >> >        regMap.put(id, reg);
> >> >
> >> > The transMgr variable above is looking up the Aries transaction
> manager
> >> > deployed in SMX (same one my JMS code is getting through the
> >> > PlatformTransactionManager interface).
> >> >
> >> > The biggest challenge I've had is that every single camel transaction
> >> > example I've seen starts the transaction INSIDE camel. They all
> resemble
> >> > the diagram on page 300 of Claus' book. I haven't seen any example
> where
> >> > camel is enlisted in an already existing transaction. I was hoping
> that
> >> was
> >> > just because examples are traditionally simple but maybe it wasn't
> >> designed
> >> > to do that?
> >> >
> >>
> >> Camel does not have its own TX manager etc. All we do is to hook into
> >> the Spring TX manager.
> >> So if there is already a TX in progress, then Camel should just play
> >> along, and run in that same TX.
> >>
> >> The Camel processing occurs in a Spring TX template in its -
> >> doInTransaction method. That happens when you use the <transacted> in
> >> the Route.
> >>
> >> > Chris
> >> >
> >> > On Thu, Apr 5, 2012 at 1:11 AM, Christian Müller <
> >> > christian.muel...@gmail.com> wrote:
> >> >
> >> >> Chris,
> >> >> may be the source code of Claus book "Camel in Action" is helpful for
> >> you
> >> >> [1].
> >> >>
> >> >> Could you als share your datasource configuration with us? It was
> not in
> >> >> your post...
> >> >>
> >> >> [1]
> >> >>
> >> >>
> >>
> http://code.google.com/p/camelinaction/source/browse/trunk/chapter9/xa/src/test/resources/spring-context.xml
> >> >>
> >> >> Best,
> >> >> Christian
> >> >>
> >> >> On Thu, Apr 5, 2012 at 7:13 AM, Chris Geer <ch...@cxtsoftware.com>
> >> wrote:
> >> >>
> >> >> > We are building an application using ServiceMix (CXF, Camel,
> Karaf...)
> >> >> and
> >> >> > we've run into an issue with transactions not propagating to camel
> >> routes
> >> >> > as we'd like them to. We have several OSGI components that run
> under
> >> >> > transactions using the Aries transaction management like the
> >> following:
> >> >> >
> >> >> >     <bean id="serviceBean" class="<class>">
> >> >> >        <property name="dataSource" ref="ds"/>
> >> >> >        <property name="camelContext" ref="camelCtx"/>
> >> >> >        <tx:transaction method="updateAddress, createAddress,
> >> >> > deleteAddress" value="Required" />
> >> >> >        <tx:transaction method="getAddress, findAddresses"
> >> >> value="Supports"
> >> >> > />
> >> >> >    </bean>
> >> >> >
> >> >> > We have published a DataSource which is transaction aware for our
> >> >> > components to use. It shows up in SMX as the following:
> >> >> >
> >> >> > aries.xa.aware = true
> >> >> > dsName = ds
> >> >> > objectClass = javax.sql.DataSource
> >> >> > service.id = 298
> >> >> >
> >> >> > In our components we are able to perform database transactions that
> >> >> > successfully get committed/rolled back as expected without having
> to
> >> >> > manually enlist the JDBC connection. It works great. Those same
> >> >> components
> >> >> > also will send various JMS messages as they succeed/fail. Our goal
> is
> >> >> that
> >> >> > if a component sends a JMS message on success and later rolls back
> the
> >> >> JMS
> >> >> > message would be retracted. If we lookup a JMS ConnectionFactory,
> >> create
> >> >> a
> >> >> > connection, session, manually enlist the session into the current
> >> >> > transaction and send the message all in code it actually works as
> >> >> desired.
> >> >> >
> >> >> > What we hope to be able to do however is to remove the code and use
> >> camel
> >> >> > instead to process the message and pass it along to the JMS topic,
> in
> >> the
> >> >> > same transaction that the OSGI component is running in but we can't
> >> quite
> >> >> > get it to work. Below is our latest configuration and code and at
> this
> >> >> > point the message posts to the topic but never rolls back.
> >> >> >
> >> >> > Blueprint File
> >> >> >    <bean id="activemq"
> >> >> > class="org.apache.activemq.camel.component.ActiveMQComponent">
> >> >> >        <property name="connectionFactory"
> >> ref="jmsXaConnectionFactory"/>
> >> >> >        <property name="transacted" value="true"/>
> >> >> >        <property name="transactionManager"
> >> ref="jmsTransactionManager"/>
> >> >> >    </bean>
> >> >> >
> >> >> >    <bean id="mandatory"
> >> >> > class="org.apache.camel.spring.spi.SpringTransactionPolicy">
> >> >> >        <property name="transactionManager"
> >> ref="jmsTransactionManager"/>
> >> >> >        <property name="propagationBehaviorName"
> >> >> > value="PROPAGATION_MANDATORY"/>
> >> >> >    </bean>
> >> >> >
> >> >> >    <bean id="jmsXaConnectionFactory"
> >> >> >          class="org.apache.activemq.ActiveMQXAConnectionFactory">
> >> >> >        <property name="brokerURL" value="tcp://localhost:61616"/>
> >> >> >    </bean>
> >> >> >
> >> >> >    <reference id="jmsTransactionManager"
> >> >> >
> >> interface="org.springframework.transaction.PlatformTransactionManager"/>
> >> >> >
> >> >> >
> >> >> >    <camel:camelContext id="camelCtx" trace="true">
> >> >> >        <camel:route>
> >> >> >            <camel:from uri="direct:genEvent"/>
> >> >> >            <camel:wireTap uri="direct:wireTap"/>
> >> >> >            <camel:transacted ref="mandatory"/>
> >> >> >            <camel:to uri="activemq:topic:event-notifications"/>
> >> >> >        </camel:route>
> >> >> >
> >> >> >        <camel:route>
> >> >> >            <camel:from uri="direct:wireTap"/>
> >> >> >            <camel:to uri="log:logger?showAll=true"/>
> >> >> >        </camel:route>
> >> >> >    </camel:camelContext>
> >> >> >
> >> >> > Code:
> >> >> >
> >> >> >        ProducerTemplate pt = camelCtx.createProducerTemplate();
> >> >> >
> >> >> >        Map<String, Object> headers = new HashMap<String, Object>();
> >> >> >        headers.put("EventType", eventType);
> >> >> >        headers.put("ClientID", 0);
> >> >> >        headers.put("EntityType", "Address");
> >> >> >
> >> >> >        pt.sendBodyAndHeaders("direct:genEvent",
> getAddress(addressID),
> >> >> > headers);
> >> >> >
> >> >> >
> >> >> > Like I mentioned, the code all works in the success case but
> doesn't
> >> >> > rollback the JMS message in the failure case. Apparently the
> >> transaction
> >> >> > context isn't being passed on to the camel route even though it's
> >> using
> >> >> the
> >> >> > same transaction manager under the covers. Is that by design or is
> >> there
> >> >> a
> >> >> > way to make this scenario work? We'd really like to be able use the
> >> camel
> >> >> > route approach so we can do more complex things than what I show
> here.
> >> >> >
> >> >> > Thanks,
> >> >> > Chris
> >> >> >
> >> >>
> >>
> >>
> >>
> >> --
> >> Claus Ibsen
> >> -----------------
> >> CamelOne 2012 Conference, May 15-16, 2012: http://camelone.com
> >> FuseSource
> >> Email: cib...@fusesource.com
> >> Web: http://fusesource.com
> >> Twitter: davsclaus, fusenews
> >> Blog: http://davsclaus.blogspot.com/
> >> Author of Camel in Action: http://www.manning.com/ibsen/
> >>
>
>
>
> --
> Claus Ibsen
> -----------------
> CamelOne 2012 Conference, May 15-16, 2012: http://camelone.com
> FuseSource
> Email: cib...@fusesource.com
> Web: http://fusesource.com
> Twitter: davsclaus, fusenews
> Blog: http://davsclaus.blogspot.com/
> Author of Camel in Action: http://www.manning.com/ibsen/
>

Reply via email to