How to always intercept a Spring 4.x transaction - java

We're using Spring 4.x, Hibernate 5.x, Spring-Data 1.11 and we currently have a SQL interceptor that extends org.hiberate.EmptyInterceptor and we basically manually hook that up at the start of the web request using HibernateInterceptor.setInterceptor. We also have jobs that run in the background via Spring task scheduler. These start their own transactions that obviously don't get the interceptor attached to them. What I'm trying to do at this point is to find a way to intercept Spring's #Transactional in all cases.
I've looked into TransactionInterceptor, and #TransactionalEventListener and so far haven't gotten any of them to work, and it's hard to figure out what is currently considered best practice with Spring.
So basically the problem we're trying to solve is that a the end of a transaction we need to know if it failed or succeeded.
So what is the current best practice with Spring to always get pre/post commit events so we can respond as needed?
edit
Realized that the #TransactionalEventListener wouldn't work as we're not using Spring events so it was just a misunderstanding on my part of what that really did.

One way you could do it is to create a simple aspect, something like:
#Aspect
#Component
public class AfterTransactionalAspect {
#After("#annotation(Transactional)")
public void cleanupAfterTransaction(JoinPoint joinPoint) throws Throwable {
// ... Do cleanup work here
}
Another good way to go (if you're using Spring-destined events) would be to use the #TransactionalEventListener.
Are you sure you're using an ApplicationEventPublisher to publish the events? Do you for sure have #EnableTransactionManagement on your config, #TransactionalEventListener is on a public method, TransactionTemplate is set, using #Transactional on the method the publish event occurs?

Related

Micronaut, db-scheduler: No current transaction present. Consider declaring #Transactional on the surrounding method

I'm trying to use db-scheduler with Micronaut. Therefore, I created a #Singleton service where I inject the actual DataSource which is of type TransactionAwareDataSource. I then call a certain method to setup the scheduler which is something like:
#Transactional
public void createJob() {
RecurringTask<Void> hourlyTask = Tasks.recurring("my-hourly-task", FixedDelay.ofHours(1))
.execute((inst, ctx) -> {
System.out.println("Executed!");
});
final Scheduler scheduler = Scheduler
.create(dataSource)
.startTasks(hourlyTask)
.threads(5)
.build();
scheduler.start();
}
which, at "create" throws this exception:
io.micronaut.transaction.exceptions.NoTransactionException: No current transaction present. Consider declaring #Transactional on the surrounding method
at io.micronaut.transaction.jdbc.TransactionalConnectionInterceptor.intercept(TransactionalConnectionInterceptor.java:65)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:96)
at io.micronaut.transaction.jdbc.TransactionalConnection$Intercepted.getMetaData(Unknown Source)
at com.github.kagkarlsson.scheduler.jdbc.AutodetectJdbcCustomization.<init>(AutodetectJdbcCustomization.java:40)
at com.github.kagkarlsson.scheduler.SchedulerBuilder.lambda$build$0(SchedulerBuilder.java:190)
at java.base/java.util.Optional.orElseGet(Optional.java:369)
Everywhere else in my app everything is working like it should, means, I can read and write to the DB via the repositories and #Transactional is working as well.
I'm not 100% sure where the problem is, but I guess it does have to do with placing the annotation. Which - in this case - is nothing I can really change. On the other hand, if I create the datasource manually, effectively bypassing micronaut, it's working.
BTW: the exception comes up within db-scheduler where the first call to the DB is made (c.getMetaData().getDatabaseProductName()).
Micronaut-Version: 2.3.4, Micronaut-Data: 2.2.4, everything setup properly.
Do you guys have any ideas how to solve this problem? Or is it even a bug?
Thanks!
So the problem is that Micronaut Data wraps the DataSource into a TransactionAwareDataSource, as you mentioned. Your library db-scheduler or mine JobRunr picks it up, and operates without the required annotations. The solution is to unwrap it before giving it to the db-scheduler or JobRunr:
Kotlin:
val unwrappedDataSource = (dataSource as DelegatingDataSource).targetDataSource
Java:
DataSource unwrappedDataSource = ((DelegatingDataSource) dataSource).targetDataSource

How to integrate a self-test into Spring-Integration?

I want to start a selftest after spring-integration is started. My first approach was to start it after the setup of the integration flow:
#Configuration
#EnableIntegration
#EnableIntegrationManagement
#IntegrationComponentScan
public class FlowConfig {
...
#PostConstruct
public void startSelfTest() {
SelfTest selfTest = new SelfTest(rezeptConfig, dataSource, archiveClient);
selfTest.run();
}
...
}
This does not work because when the test was started the tables in the database were missing because liquibase was not yet started. I guess the liquibase scripts would be started after initialization.
Any ideas what is the best place to start a selftest?
Well, the best practice to do low-level resources interaction is when everything is initialized in the application context already. And that is the phase when beans are started according their SmartLifecycle implementation.
So, what I suggest to revise your solution to be done from some SmartLifecycle.start().
That's exactly what we do everywhere around in Spring Integration.
(Be sure that we talk exactly about the same Spring Integration: https://spring.io/projects/spring-integration)
See more info in docs: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-lifecycle-processor
just guessing, what about onApplicationEvent event in ApplicationListener ? This is called when Spring is initialized and ready.
E.g. check this one How to add a hook to the application context initialization event?
The Liquibase bean which is responsible to create and update the DB tables are started after my Selftest. One solution is to use #DependsOn togehther with the #Bean annotation:
#Bean
#DependsOn("liquibase")
public SelfTest startSelfTest() {
SelfTest selfTest = new SelfTest(rezeptConfig, dataSource, archiveClient);
selfTest.run();
return selfTest;
}
Now the Selftest is started after Liquibase.

Spring Boot - metrics for MeterBinder never registered

Situation: You have a metric registered in Spring Boot via a MeterBinder. Maybe it one of the auto-configured metrics like jvm.gc.pause1 or it could be a custom metric of your own. But one day, you start your application and it is missing. It isn’t reported, it doesn’t show in Actuator, it’s just gone.
Root Cause: Probably your code or a library you are using is injecting the MeterRegistry. There are lots of legitimate reasons to do this, so don’t blame yourself. But injecting the MeterRegistry means that it will be created and initialised before all your beans are created, including possible MeterBinders.
It is also possible nothing is injecting MeterRegistry, but Spring has decided to create it before the MeterBinders for some other reason. Whatever the case, MeterBinders will stop working for you and there isn’t much you can do about it.
My solution is to create my own post-processor:
#Component
class FixMeterBinders implements BeanPostProcessor {
#Autowired
ObjectProvider<MeterRegistry> meters;
public Object postProcessAfterInitialization(Object bean, String beanName) {
if(bean instanceof MeterBinder) {
((MeterBinder)bean).bindTo(meters.getObject());
}
return bean;
}
}
There is a big downside to this approach: If Spring’s post-processor is working as intended, each MeterBinder will be run twice, so you need to make sure the work they do is idempotent.

#HystrixCommand + Inject Errors with Aspect

We're using Hystrix like so:
#HystrixCommand(...)
public void someOperation() {
...
}
This works great. We'd like to be able to inject errors and sleeps for testing within these methods and we're trying to create an #Aspect for just this purpose:
#Before("execution(* our.package.OurClass.someOperation(..))")
public void causeTrouble() {
...
}
The issue we're seeing is that our advice is running before the Hystrix advice (HystrixCommandAspect) which means that sleeps and exceptions we inject in are treated differently and not handled by Hystrix. Is there a way to ensure our aspect runs inside the Hystrix aspect? I've tried the ordering suggestions in the spring documentation without any luck (see http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj-advice-ordering). Is there another approach to solve this? Thanks.

How to call a custom rollback method in Spring Transaction Management?

Environment: Spring 3, Custom Transaction Management, JDBC Transactions
I just read the Spring docs on using the transaction template to handle transaction management. It seemed overly complex so I want to ask:
Most of my transactions are JDBC related, meaning I just declare an #Transactional on my service. But now I am making a REST service call to another site which needs to rollback if any of the following JDBC operations fail, I'll provide the rollback code in this case.
As I progress in my method, in my transaction - I want to save a reference to the REST service call (needed to roll back that action), and upon exception I just want a method myCustomRollback() called which can access the previously stored object.
Why not just provide a map in the transactionTemplate for storing stuff and define a custom rollback method on the #Transactional annotation?
This is the way I think about it, I'm not following the way Spring thinks about this. Can someone help me bridge the gap between what I want and how I accomplish it most efficiently in Spring? I only need to do this for a few special case operations.
To anyone still reading this:
I solved a similar problem with spring events - as suggested by Den Roman in option 3.
Here's the basic idea (scenario is fictional):
Whenever I perform external operations that need to be rolled back together with the transaction, I publish an event inside my #Transactional method using support from spring (org.springframework.context.ApplicationEventPublisher):
#Transactional
public String placeOrder(Order order) {
String orderId = orderServiceGateway.createOrder(order);
applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId));
workflowService.startWorkflow(orderId);
return orderId;
}
The event itself can be any object - I created a POJO with details about the remote entity to be deleted.
Then I registered a special event listener that is bound to a transaction phase - in my case to the rollback:
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
String orderId = orderCreatedEvent.getOrderId();
orderServiceGateway.deleteOrder(orderId);
}
Of course, it's recommended to catch & log the exception from rollback operation, not to lose the original exception from the placeOrder() method.
By default these events are synchronous, but they can be made async by additional configuration.
Here's a very good article on this mechanism, including detailed configuration and pitfalls: Transaction Synchronization and Spring Application Events (DZone)
While I don't like the solution 100% because it clutters the business logic with event publishing stuff and binds to spring, it definitely does what I expect it to do and makes it possible to pass context from the transactional method to the rollback method - which is not available through a traditional try/catch block outside of the transactional method (unless you put your context in the exception itself, which is not very nice).
I've re-read your question a few times and am not sure I understand your question completely. I assume your executing someCode and if that fails you would like to execute myCustomRollback which has some information about someCode. So I'll try to provide a Generic answer.
If you want spring to rollback some code. It will only rollback that which is rollBackAble, like jdbc transactions. Assume you have a method which performs 2 calls.
#Transactional
public void doStuff(SomeEntity entity, File file) {
persist(entity);
customFileService.createOnFileSystem(file);
throw new RunTimeException();
}
So the code above will always rollback. It will undo the persisting of your entity, but not the creation of your file, since that is not managed by Spring transactions, unless you provide custom implementation for it to be.
Second, Spring provides 2 ways of working with transactions:
Spring AOP: a proxy is created at runtime which will decorate your code with transactional stuff. If your class would be named MyClass, then Spring will create a class names MyClassProxy, which will wrap your code in transactional code.
AspectJ: at compile time your .class file will be adjusted and transactional code will be embedded inside your method.
The aspectJ approach seems harder to configure, but isn't so much and is way easier to use. Since anything which is annotated with #Transactional will be embedded (weaved) with code. For Spring AOP this is not the case. Transactional inner method calls for instance in Spring will be ignored! So aspectJ provides a more intuitive approach.
Back to what I think your question is (the code is all in 1 class):
public void doSomeCode() {
Object restCall = initialize();
try {
execute(restCall);
} catch (CustomException e) {
myCustomRollback(restCall; e);
}
}
#Transactional(rollbackFor = CustomException.class)
private void execute(Object restCall) throws CustomException {
// jdbc calls..
restCall = callRest(restCall);
throw new CustomException();
}
void myCustomRollback(Object restCall, CustomException e) {
...
}
The code above will only work with AspectJ! Since your making inner method calls which also seems to be private! AOP at runtime cannot handle this.
So what happens is everything (which is rollbackAble) in execute will be rollbacked. And in doStuff you have information about the objects which were used in execute, you now can use in myCustomRollback to rollback your REST stuff manually.
Not sure if I answered this question properly, but I hope it helps someone with a similar problem.
1 solution is to implement your own transactional manager by extending a one
2 solution is to use TransactionSynchronizationManager class
3 solution is to use #TransactionalEventListener in case you have Spring 4
Spring transaction management the default behavior for automatic rollback is for unchecked exceptions
so for a custom exception,
#Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class)
public void doSomething(...
)
the transaction be rolled back if it there is an exception that matches the specified. If an exception not matches, it is propagated to caller of the service or TransactionRolledBackException wrapper
if you use use the org.springframework.transaction.PlatformTransactionManager it is more manageable handling exceptions than template
check the documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
you can use the AfterThrowing advice (when an exception is thrown) & call your method (myCustmRollback()) there, you can use TransactionSynchronizationManager class to get thecurrent transaction & roll it back...
alternatively.. you can use the AroundAdvice to begin & commit/rollback your transaction (this way you can use the spring provided transaction manager by using the TransactionSynchronizationManager class)

Categories