Register beforeCommit callback for all Spring managed transactions - spring

I am using the ChainedTransactionManager to implement Best Effort 1PC across ActiveMQ and MySQL, in my case the Database transaction commits first. In order to reduce the window of failure I want to check whether the ActiveMQConnection has failed just before committing the database transaction. I can do this once a transaction has started with the TransactionSynchronizationManager.registerSynchronization, but what I want is to register a block of code that runs for every transaction without having to do it in my code.
I could just subclass the ChainedTransactionManager but this does not seem the cleanest. Is there a better way to do this?
EDIT: Looks like subclassing ChainedTransactionManager is not a good idea as it relies on MultiTransactionStatus which is not public. Creating a new PlatformTransactionManager that delegates to a ChainedTransactionManager is an alternative.

This is how I have decided to implement it:
import org.apache.activemq.ActiveMQConnection;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.jms.connection.JmsResourceHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class JmsTransportFailureHandlingTransactionManager implements PlatformTransactionManager {
private final ChainedTransactionManager chainedTransactionManager;
public JmsTransportFailureHandlingTransactionManager(ChainedTransactionManager chainedTransactionManager) {
this.chainedTransactionManager = chainedTransactionManager;
}
#Override
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
TransactionStatus transaction = chainedTransactionManager.getTransaction(definition);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void beforeCommit(boolean readOnly) {
for (Object resource : TransactionSynchronizationManager.getResourceMap().values()) {
if (resource instanceof JmsResourceHolder) {
ActiveMQConnection connection = (ActiveMQConnection) ((JmsResourceHolder) resource).getConnection();
if (connection.isTransportFailed()) {
throw new IllegalStateException("ActiveMQ transport failed.");
}
}
}
}
});
return transaction;
}
#Override
public void commit(TransactionStatus status) throws TransactionException {
chainedTransactionManager.commit(status);
}
#Override
public void rollback(TransactionStatus status) throws TransactionException {
chainedTransactionManager.rollback(status);
}
}

Related

Extending a exception aspect in order to have application specific implementation

in my spring boot application, I have been using one external commons library for handling exceptions. The external library has an aspect defined for the same something like below:
#Aspect
#Order(0)
public class InternalExceptionAspect {
public InternalExceptionAspect() {
}
#Pointcut("#within(org.springframework.stereotype.Service)")
public void applicationServicePointcut() {
}
#AfterThrowing(
pointcut = "applicationServicePointcut()",
throwing = "e"
)
public void translate(JoinPoint joinPoint, Throwable e) {
String resourceId = this.getResourceId();
if (e instanceof BadInputException) {
BadInputException inputException = (BadInputException)e;
throw new BadRequestAlertException(inputException.getErrorCode().getDefaultMessage(), inputException.getMessage(), inputException.getErrorCode().getHttpStatusCode(), resourceId, inputException.getErrorCode().getCode());
} else if (!(e instanceof BadServerStateException) && !(e instanceof InternalException)) {
String message;
if (e instanceof JDBCException) {
...
throw new BadServerStateException(message, resourceId, "20");
} else {
...
throw new BadServerStateException(message, resourceId, "10");
}
} else {
InternalException serverStateException = (InternalException)e;
throw new BadServerStateException(serverStateException.getErrorCode().getDefaultMessage(), serverStateException.getMessage(), resourceId, serverStateException.getErrorCode().getHttpStatusCode(), serverStateException.getErrorCode().getCode(), serverStateException.getErrorCode().getErrorType().name());
}
}
String getResourceId() {
RequestHeaders requestHeaders = RequestResponseContext.getRequestHeaders();
return requestHeaders.getResourceId();
}
}
Here I would like to introduce another else if block in order to handle DuplicateKeyException for my application.
The problem is, the above code, being part of the commons library is being used by multiple other applications. But, I would like to do the change to be applied only in my application.
I have been thinking to inherit the Aspect class something like below, inside my application:
#Aspect
#Order(0)
public class MyInternalExceptionAspect extends InternalExceptionAspect {
public MyInternalExceptionAspect() {
}
#Pointcut("#within(org.springframework.stereotype.Service)")
public void applicationServicePointcut() {
}
#AfterThrowing(
pointcut = "applicationServicePointcut()",
throwing = "e"
)
public void translate(JoinPoint joinPoint, Throwable e) {
if(e instanceof DuplicateKeyException) {
...
}
super.translate(joinpoint, e);
}
}
But, I am not sure, if this is the correct approach to do this. Could anyone please help here regarding what would be the best approach to achieve this? Thanks.
You cannot extend a concrete aspect using class MyInternalExceptionAspect extends InternalExceptionAspect. It will cause an exception in Spring AOP:
...AopConfigException:
[...MyInternalExceptionAspect] cannot extend concrete aspect
[...InternalExceptionAspect]
Only abstract aspects are meant to be extended.
But you can simply create a new aspect without inheritance and make sure that it has a lower priority than the original aspect.
Why lower priority?
Acording to the #Order javadoc, "lower values have higher priority".
You want your own aspect's #AfterReturning advice to kick in before the original aspect possibly transforms the exception of interest into something else, before you have a chance to handle it. But according to the Spring manual: "The highest precedence advice runs first "on the way in" (so, given two pieces of before advice, the one with highest precedence runs first). "On the way out" from a join point, the highest precedence advice runs last (so, given two pieces of after advice, the one with the highest precedence will run second).".
Therefore, your own aspect should have #Order(1), giving it lower priority than the original aspect, but making the #AfterThrowing advide run before the original one. Sorry for the reverse logic, even though it makes sense. You just need to be aware of it.
Here is an MCVE, simulating your situation in a simplified way:
package de.scrum_master.spring.q69862121;
import org.springframework.stereotype.Service;
#Service
public class MyService {
public void doSomething(Throwable throwable) throws Throwable {
if (throwable != null)
throw throwable;
}
}
package de.scrum_master.spring.q69862121;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
#SpringBootApplication
#Configuration
public class DemoApplication {
private static final Logger log = LoggerFactory.getLogger(DemoApplication.class.getName());
public static void main(String[] args) throws Throwable {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
MyService myService = appContext.getBean(MyService.class);
List<Throwable> throwables = Arrays.asList(
null, // No exception -> no aspect should kick in
new Exception("oops"), // Not covered by any aspects -> no translation
new IllegalArgumentException("uh-oh"), // Original aspect translates to RuntimeException
new NullPointerException("null"), // Custom aspect translates to RuntimeException
new ArithmeticException("argh") // Custom aspect translates to IllegalArgumentException,
// then original aspect translates to RuntimeException
);
for (Throwable originalThrowable : throwables) {
try {
myService.doSomething(originalThrowable);
}
catch (Throwable translatedThrowable) {
log.info(translatedThrowable.toString());
}
}
}
}
As you can see, the application calls the service, the first time with null, not causing any exception, then with several types of exceptions the aspects are meant to either ignore or translate.
package de.scrum_master.spring.q69862121;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
#Aspect
#Component
#Order(0)
public class InternalExceptionAspect {
private static final Logger log = LoggerFactory.getLogger(InternalExceptionAspect.class.getName());
#AfterThrowing(pointcut = "#within(org.springframework.stereotype.Service)", throwing = "e")
public void translate(JoinPoint joinPoint, Throwable e) {
log.info(joinPoint + " -> " + e);
if (e instanceof IllegalArgumentException)
throw new RuntimeException("Transformed by InternalExceptionAspect", e);
}
}
package de.scrum_master.spring.q69862121;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
#Aspect
#Component
#Order(1)
public class MyInternalExceptionAspect {
private static final Logger log = LoggerFactory.getLogger(MyInternalExceptionAspect.class.getName());
#AfterThrowing(pointcut = "#within(org.springframework.stereotype.Service)", throwing = "e")
public void translate(JoinPoint joinPoint, Throwable e) {
log.info(joinPoint + " -> " + e);
if (e instanceof NullPointerException)
throw new RuntimeException("Transformed by MyInternalExceptionAspect", e);
if (e instanceof ArithmeticException)
throw new IllegalArgumentException("Transformed by MyInternalExceptionAspect", e);
}
}
The console log proves that everything works as expected, also with regard to invocation order:
d.s.s.q.MyInternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.Exception: oops
d.s.s.q69862121.InternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.Exception: oops
d.s.spring.q69862121.DemoApplication : java.lang.Exception: oops
d.s.s.q.MyInternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.IllegalArgumentException: uh-oh
d.s.s.q69862121.InternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.IllegalArgumentException: uh-oh
d.s.spring.q69862121.DemoApplication : java.lang.RuntimeException: Transformed by InternalExceptionAspect
d.s.s.q.MyInternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.NullPointerException: null
d.s.s.q69862121.InternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.RuntimeException: Transformed by MyInternalExceptionAspect
d.s.spring.q69862121.DemoApplication : java.lang.RuntimeException: Transformed by MyInternalExceptionAspect
d.s.s.q.MyInternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.ArithmeticException: argh
d.s.s.q69862121.InternalExceptionAspect : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable)) -> java.lang.IllegalArgumentException: Transformed by MyInternalExceptionAspect
d.s.spring.q69862121.DemoApplication : java.lang.RuntimeException: Transformed by InternalExceptionAspect

Spring cloud stream : how to use #Transactional with new Consumer<> functional programming model

I have StreamListener which I would like to replace using the new functional model and Consumer <>. Unfortunately, I don't know how to transfer #Transactional to new model:
#Transactional
#StreamListener(PaymentChannels.PENDING_PAYMENTS_INPUT)
public void executePayments(PendingPaymentEvent event) throws Exception {
paymentsService.triggerInvoicePayment(event.getInvoiceId());
}
I have tired certain things. Sample code below. I added logging messages to a different queue for tests. Then I throw an exception to trigger a rollback. Unfortunately, messages are queued even though they are not there until the method is completed (I tested this using brakepoints). It seems that the transaction was automatically committed despite the error.
#Transactional
#RequiredArgsConstructor
#Component
public class functionalPayment implements Consumer<PendingPaymentEvent> {
private final PaymentsService paymentsService;
private final StreamBridge streamBridge;
public void accept(PendingPaymentEvent event) {
paymentsService.triggerInvoicePayment(event.getInvoiceId());
streamBridge.send("log-out-0",event);
throw new RuntimeException("Test exception to rollback message from log-out-0");
}
}
Configuration:
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.queue-name-group-only=true
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.declare-exchange=true
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.bind-queue=true
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.transacted=true
spring.cloud.stream.source=log
spring.cloud.stream.bindings.log-out-0.content-type=application/json
spring.cloud.stream.bindings.log-out-0.destination=log_a
spring.cloud.stream.bindings.log-out-0.group=log_a
spring.cloud.stream.rabbit.bindings.log-out-0.producer.declare-exchange=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.bind-queue=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.queue-name-group-only=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.binding-routing-key=log
spring.cloud.stream.rabbit.bindings.log-out-0.producer.transacted=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.exchange-type=direct
spring.cloud.stream.rabbit.bindings.log-out-0.producer.routing-key-expression='log'
Have you tried something along the lines of
#Transactional
public class ExecutePaymentConsumer implements Consumer<PendingPaymentEvent> {
public void accept(PendingPaymentEvent event) {
paymentsService.triggerInvoicePayment(event.getInvoiceId());
}
}
. . .
#Bean
public ExecutePaymentConsumer executePayments() {
return new ExecutePaymentConsumer();
}

Multiple RedisConnectionFactories in Spring Boot Application

My application uses one "main" redis instance for things like session storage and cache but needs to talk to a separate "external" instance for other reasons. I am trying to determine the "best" ("most idiomatic"? "simplest"?) way to configure this in my Spring Boot application.
Ideally I'd just like to use the default auto-configuration for the main instance but as soon as I register a connection factory for the external instance the #ConditionalOnMissngBean({RedisConnectionFactory.class}) condition in LettuceConnectionConfiguration becomes false and so the default instance isn't created. Looking at what else is going on in LettuceConnectionConfiguration etc. I feel like I'd rather not manually configure it if I don't need to.
I could just not expose the "external" connection factory as a bean and only use it internally to create the beans that depend on it but, while that would be ok in my specific case, I'd like to understand if there's a better solution where both factories can be exposed.
Is there some way I can expose the second RedisConnectionFactory without disabling the default one provided by auto configuration? Is there a clear "right way" to do this sort of thing?
you must implement the BeanDefinitionRegistryPostProcessor to adjust the RedisConnectionFactory order
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;
#Component
public class MultipleRedisConnectionFactoryRegistrar implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition bd1 = registry.getBeanDefinition("redisConnectionFactory");
if (bd1 != null) {
BeanDefinition bd = new RootBeanDefinition(ExternalRedisConnectionFactoryBean.class);
registry.registerBeanDefinition("externalRedisConnectionFactory", bd);
}
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
in ExternalRedisConnectionFactoryBean, you can create your own RedisConnectionFactory
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
public class ExternalRedisConnectionFactoryBean implements FactoryBean<RedisConnectionFactory> {
#Override
public RedisConnectionFactory getObject() throws Exception {
//you can mannually create your external redis connection factory here
return null;
}
#Override
public Class<?> getObjectType() {
return RedisConnectionFactory.class;
}
}
if you want to use the multiple RedisConnectionFactory, you #Qualifier is the right choice, for example
#Autowired
#Qualifier("redisConnectionFactory")
private RedisConnectionFactory defaultRedisConnectionFactory;
#Autowired
#Qualifier("externalRedisConnectionFactory")
private RedisConnectionFactory externalRedisConnectionFactory;

How to involve a Collection on a spring transaction?

I currently have a spring application with hibernate and a PlataformTransactionManager running on Jboss/wildfly.
Some of the methods that manipulate the database also call a bean which contains a LinkedBlockingQueue. This queue stores logging messages that are periodically dispatched to someplace else on another thread (using simple spring #Scheduler).
Would it be possible to make my queue (inside a bean) transactional? ie. if the transaction rollback would I be able to "undo" any operations made on my Collection? What's the best strategy to implement this ?
So, in short something like:
#Service
#Transactional
public PersonService {
#Autowired
EntityManager EM;
#Autowired
LoggingBuffer logger;
public void addPerson(String name) {
EM.persist(new Person(.....));
logger.add("New person!");
// A rollback here via some thrown exception would not affect the queue
}
}
#Component
public class LoggingBuffer {
private Queue<String> q= new LinkedBlockingQueue<String>();
public add(String msg){
q.add(msg);
}
}
Try something like this
#Transactional
public void addPerson(String name) {
EM.persist(new Person(.....));
//logger.add("New person!");
// A rollback here via some thrown exception would not affect the queue
}
public void wrapAddPerson(String name){
List<String> localBuffer = new ArrayList<>();
try{
addPerson(name);
localBuffer.add(".....");
}catch(Exception e)
{
localBuffer.clear();
}
finally{
localBuffer.forEach(logger::add);
}
}

Spring PostConstruct of a container

How can I run some code inside a Spring Container after all beans has been loaded? I know I can use #PostConstruct for a single bean, but I would like to run that piece of code after all PostConstructs are called.
Is is possibile?
---UPDATE---
I tried to follow the ApplicationListener way, this is the implementation:
#Component
public class PostContructListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger log = LoggerFactory.getLogger(PostContructListener.class);
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Collection<Initializable> inits= contextRefreshedEvent.getApplicationContext().getBeansOfType(Initializable.class).values();
for (Initializable initializable : inits) {
try{
log.debug("Initialization {} ",initializable.getClass().getSimpleName());
initializable.init();
}catch(Exception e){
log.error("Error initializing {} ",initializable.getClass().getSimpleName(),e);
}
}
}
}
Applying "Initializable" interface to all services I got what I needed, how every this way I broke all autowires, I cannot understand why but seems to be connected to the new "Initializable" interface:
java.lang.IllegalArgumentException: Can not set com.service.MyService field com.controller.RestMultiController.myService to com.sun.proxy.$Proxy41
I think you need this.
public class SpringListener implements ApplicationListener<ContextRefreshedEvent>{
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent ) {
// do things here
}
}
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events

Resources