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/ >