Narayana compatibility with spring boot 2.4.5

215 views
Skip to first unread message

Vytautas Kalinauskas

unread,
Jun 30, 2021, 4:38:21 AM6/30/21
to narayana-users
Good morning, I have been trying to implement XA transactions for one of my rest endpoints (persist to db and send JMS message). As I can see from the logs while debugging db (Postgresql) is using arjuna as it is supposed to, but when it comes to publishing message to activeMQ by using camel I am getting exception:

javax.jms.JMSException: Session's XAResource has not been enlisted in a distributed transaction. at org.apache.activemq.ActiveMQXASession.doStartTransaction(ActiveMQXASession.java:101) at org.apache.activemq.ActiveMQSession.send(ActiveMQSession.java:1916) at org.apache.activemq.ActiveMQMessageProducer.send(ActiveMQMessageProducer.java:288) at org.apache.activemq.ActiveMQMessageProducer.send(ActiveMQMessageProducer.java:223) at org.apache.activemq.ActiveMQMessageProducerSupport.send(ActiveMQMessageProducerSupport.java:241)

Is Narayana compatible with spring boot 2.4.5 at all? If so what could be the problem behind this exception? 

Here is my controller code

@ResponseBody
@Transactional
public ResponseEntity<ReceiptDetailResource> createReceiptDetail(
@RequestHeader("X-TenantId") String tenantId,
@RequestHeader("X-CompanyId") Long companyId,
@RequestHeader("X-CashMachineId") Long cashMachineId,
@Valid @RequestBody ReceiptDetailResource receiptDetailDto) throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException {
log.info("Creating Receipt Detail");
ReceiptDetailResource receiptDetailResource = new ReceiptDetailResource();
try {
ReceiptDetail receiptDetail = EnrichReceiptDetail(receiptDetailResourceTransformService.fromDto(receiptDetailDto), cashMachineId);
if (tenantId == null || companyId == null || cashMachineId == null) {
log.error("Headers are not set !!!");
throw new IllegalArgumentException();
}
Optional<ReceiptHeader> receiptHeaderOptional = receiptHeaderQueryService.getRepository().findById(receiptDetailDto.getReceiptHeaderId());
if (receiptHeaderOptional.isPresent()) {
receiptDetail.setReceiptHeader(receiptHeaderOptional.get());
} else {
// If ReceiptHeader is null - create new ReceiptHeader using context.
RequestContext.getRequestContext("x-organizationid");
RequestContext.getRequestContext("x-companyid");
RequestContext.getRequestContext("x-cashmachineid");
Optional<CashRegister> cashRegister = cashRegisterQueryService.findById(Long.parseLong((String) RequestContext.getRequestContext("x-cashmachineid")));
// TODO Get client:
ReceiptHeader receiptHeader = new ReceiptHeader();
receiptHeader.setReceiptDate(ZonedDateTime.now());
receiptHeader.setCashRegister(cashRegister.get());
receiptHeader = receiptHeaderQueryService.getRepository().save(receiptHeader);
receiptDetail.setReceiptHeader(receiptHeader);
}
receiptDetail = receiptDetailQueryService.getRepository().save(receiptDetail);
ReceiptDetailDiscount receiptDetailDiscount = new ReceiptDetailDiscount();
receiptDetailResource = new ReceiptDetailResource(receiptDetail);
priceBean.applyDiscountForReceiptDetail(receiptDetailResource, receiptDetailDiscount);
receiptDetailResource.setId(receiptDetail.getId());
if(receiptDetail.getDiscountPrice() != null || receiptDetail.getDiscountPercentage() != null) {
priceBean.updateReceiptDetailDiscount(receiptDetailDiscount, receiptDetail.getId());
}
// TODO Send event:
String updatedBy = null;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
updatedBy = ((UserDetails) authentication.getPrincipal()).getUsername();
}
BasePosEvent receiptDetailCreatedEvent = new BasePosEvent(updatedBy, tenantId, companyId, null, cashMachineId,
PosSalesEventTypes.receiptDetailCreatedEvent, receiptDetailResource);
camelPublisher.publish(receiptQueue, receiptDetailCreatedEvent, PosSalesEventTypes.receiptDetailCreatedEvent.name());

log.info("Receipt Detail created: " + receiptDetailResource.getId());
return ResponseEntity.ok(receiptDetailResource);

} catch (EntityNotFoundException e) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, e.getLocalizedMessage()
);
} catch (IllegalArgumentException e) {
log.warn("Bad Receipt Detail or context.", e);
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST, "Bad Receipt Detail or context."
);
}
}

exception is thrown at this line: 
camelPublisher.publish(receiptQueue, receiptDetailCreatedEvent, PosSalesEventTypes.receiptDetailCreatedEvent.name());

Thank you for your answer

Ondra Chaloupka

unread,
Jun 30, 2021, 5:01:29 AM6/30/21
to narayana-users
Hello,

the trouble could be the integration is not properly setup. Do you use the Snowdrop Narayana starter in your project? https://github.com/snowdrop/narayana-spring-boot

When you install just Narayana as the transaction manager then Narayana is capable to start an in-memory JTA transaciton. But those are other systems that has to enlist to transaction. After the transaction is started then Narayana only manages the outcome of the transaction. I assume that DB works fine with JTA transaction as it's Hibernate who manages the enlisting to JTA transaction (receiptHeaderQueryService.getRepository().findById(...)).
which could not be available in your project.

Ondra
Reply all
Reply to author
Forward
0 new messages