Quarkus 2.8.0.Final introduced QuarkusTransaction. What is the difference between
#ApplicationScoped
public class MyClass {
#Inject
TransactionManager tm;
public void doSomething() throws Exception {
tm.begin();
// ...
tm.commit();
}
}
and
#ApplicationScoped
public class MyClass {
public void doSomething() {
QuarkusTransaction.begin();
// ...
QuarkusTransaction.commit();
}
}
?
I am using the TransactionManager in a lot of my tests, and when I replaced it with QuarkusTransaction, I am getting different error messages when something fails:
When using the TransactionManager, I am getting
javax.transaction.NotSupportedException: BaseTransaction.checkTransactionState - ARJUNA016051: thread is already associated with a transaction!
When using QuarkusTransaction, I am getting
javax.enterprise.context.ContextNotActiveException
The Quarkus documentation does not really explain why QuarkusTransaction was introcuded 🤔
QuarkusTransaction was introduced with this Pull Request and the idea is to provide an easier to use Transactions API.
As can be seen in this test, it's meant to be used when a request scope is active
Related
I want to measure time of sql execution which will be run by MyBatis (Spring Boot project) and bind that with other request parameters, so I can get full info about performance issues regarding specific requests. For that case I have used MyBatis Interceptor on following way:
#Intercepts({
#Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
#Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryMetricsMybatisPlugin implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
Stopwatch stopwatch = Stopwatch.createStarted();
Object result = invocation.proceed();
stopwatch.stop();
logExectionTime(stopwatch, (MappedStatement) invocation.getArgs()[0]);
return result;
}
}
Now when it come to binding with request, I want to store those metrics in request as attribute. I have tried this simple solution to get request, but that was not working since request was always null (I have read that this solution won't work in async methods, but with MyBatis Interceptor and its methods I think that's not the case):
#Autowired
private HttpServletRequest request;
So, the question is how properly get request within MyBatis interceptor?
One important note before I answer your question: it is a bad practice to access UI layer in the DAO layer. This creates dependency in the wrong direction. Outer layers of your application can access inner layers but in this case this is other way round. Instead of this you need to create a class that does not belong to any layer and will (or at least may) be used by all layers of the application. It can be named like MetricsHolder. Interceptor can store values to it, and in some other place where you planned to get metrics you can read from it (and use directly or store them into request if it is in UI layer and request is available there).
But now back to you question. Even if you create something like MetricsHolder you still will face the problem that you can't inject it into mybatis interceptor.
You can't just add a field with Autowired annotation to interceptor and expect it to be set. The reason for this is that interceptor is instantiated by mybatis and not by spring. So spring does not have chance to inject dependencies into interceptor.
One way to handle this is to delegate handling of the interception to a spring bean that will be part of the spring context and may access other beans there. The problem here is how to make that bean available in interceptor.
This can be done by storing a reference to such bean in the thread local variable. Here's example how to do that. First create a registry that will store the spring bean.
public class QueryInterceptorRegistry {
private static ThreadLocal<QueryInterceptor> queryInterceptor = new ThreadLocal<>();
public static QueryInterceptor getQueryInterceptor() {
return queryInterceptor.get();
}
public static void setQueryInterceptor(QueryInterceptor queryInterceptor) {
QueryInterceptorRegistry.queryInterceptor.set(queryInterceptor);
}
public static void clear() {
queryInterceptor.remove();
}
}
Query interceptor here is something like:
public interface QueryInterceptor {
Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
}
Then you can create an interceptor that will delegate processing to spring bean:
#Intercepts({
#Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }),
#Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}) })
public class QueryInterceptorPlugin implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
QueryInterceptor interceptor = QueryInterceptorRegistry.getQueryInterceptor();
if (interceptor == null) {
return invocation.proceed();
} else {
return interceptor.interceptQuery(invocation);
}
}
#Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
#Override
public void setProperties(Properties properties) {
}
}
You need to create an implementation of the QueryInterceptor that does what you need and make it a spring bean (that's where you can access other spring bean including request which is a no-no as I wrote above):
#Component
public class MyInterceptorDelegate implements QueryInterceptor {
#Autowired
private SomeSpringManagedBean someBean;
#Override
public Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
// do whatever you did in the mybatis interceptor here
// but with access to spring beans
}
}
Now the only problem is to set and cleanup the delegate in the registry.
I did this via aspect that was applied to my service layer methods (but you can do it manually or in spring mvc interceptor). My aspect looks like this:
#Aspect
public class SqlSessionCacheCleanerAspect {
#Autowired MyInterceptorDelegate myInterceptorDelegate;
#Around("some pointcut that describes service methods")
public Object applyInterceptorDelegate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
QueryInterceptorRegistry.setQueryInterceptor(myInterceptorDelegate);
try {
return proceedingJoinPoint.proceed();
} finally {
QueryInterceptorRegistry.clear();
}
}
}
For spring boot application.
I have my aspect listen on my private or public method inside my scheduled method.
But it doesn't work. However, the aspect can listen on my scheduled method.
Here is an example on my github.
https://github.com/benweizhu/spring-boot-aspect-scheduled
Does any know the answer or why? or how to resolve it?
Thanks
Aspects will not work on calling other methods within the same class as it cannot be proxied.
It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen
note on proxying private methods :
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
refer : How can I log private methods via Spring AOP?
change the code as below:
#Component
public class Test{
public void reportInPrivateMethod() {
System.out.println("private method");
}
public void reportInPublicMethod() {
System.out.println("public method");
}
}
Now invoke this method :
#Component
public class ScheduledTasks {
#Autowired
private Test test;
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
test.reportInPrivateMethod();
test.reportInPublicMethod();
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
Modify the aspects as per the changes :
#Aspect
#Component
public class Monitor {
#AfterReturning("execution(* com.zeph.aop.ScheduledTasks.reportCurrentTime())")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPrivateMethod())")
public void logServiceAccessPrivateMethod() {
System.out.println("Completed PRIVATE :");
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPublicMethod())")
public void logServiceAccessPublicMethod() {
System.out.println("Completed PUBLIC: ");
}
}
I have a code like below
public abstract class AffltTransactionService implements IAffltTransactionService {
....
#Override
#Transactional
public void processTransactions(List<? extends AffltTransaction> transactions) {
for (AffltTransaction transaction : transactions) {
if (transaction != null) {
processTransaction(transaction);
}
}
}
private void processTransaction(AffltTransaction transaction) {
try {
processTransactionInternal(transaction);
} catch (Exception exception) {
affltTransactionError = new AffltTransactionError(null, null, "error", new Date());
saveAffltTransactionError(affltTransactionError);
log.error(exception.getLocalizedMessage());
}
}
#Transactional(readOnly=false, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void processTransactionInternal(AffltTransaction transaction) {
processTransactionInternal throws ServiceUnAvailableException which extends RuntimeException
But the transaction is not getting rolled back despite having rollbackFor = Exception.class .
Can you please help.
#Transactional annotation won't have any effect if you are calling the method directly, since Spring creates proxies above annotated classes and the aspect-defined functionality is implemented by proxy. So, when you call the method from within your class it doesn't get through proxy, and hence no transcation is created and/or rolled back.
Take a look at the Spring reference for detailed explanation.
Since you invoke one method from another within the same bean, the Spring AOP doesn't use any advices in this case.
Only processTransactions is wrapped with TransactionInteceptor.
To make it worked you should configure:
<aop:aspectj-autoproxy expose-proxy="true"/>
But it isn't recommened, though.
More info here: http://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy
Use getCurrentSession instead of opensession
I know it was asked a long time ago but I faced with the same issue and for me was missing Spring configurantion annotation:
#EnableTransactionManagement
After write it on ApplicationConfiguration class it was solved. I hope it helps someone on future.
The method you use apply #Transactional should throws exception. Don't use try-catch instead.(I guess you use try-catch in somewhere in your processTransaction function).
Code should be like this:
#Transactional
public void processTransactions(List<? extends AffltTransaction> transactions) threws Exception{
for (AffltTransaction transaction : transactions) {
if (transaction != null) {
processTransaction(transaction);
}
}
}
I am fairly new to Sprint and am using Spring 3.x and roo1.1.1 for my application.
I have multiple implementation of an interface which would be #Autowired into other different classes. I would only be able to decide which implementation to go with at the runtime. This should be achieved with like a factory pattern.
public interface SomeInterface {
public void doSomething();
}
Implementation 1.
public class SomeOb implements SomeInterface {
public void doSomething() {
//Do something for first implementation here
}
}
Implementation 2.
public class SomeOtherOb implements SomeInterface {
public void doSomething() {
//Do something for first implementation here
}
}
Now in my service i needed this Autowired like
#Service
public class MyService {
#Autowired
SomeInterface ob;
//Rest of the code here
}
1) The logic to choose which implementation to be Autowired is only know runtime, so i cannot use the #Qualifier annotation to qualify this.
2) I tried to create a FactoryBean like
public class SomeFactoryBean implements FactoryBean<SomeInterface> {
#Override
public SomeInterface getObject() throws Exception {
if(/*Somecondition*/) {
return new SomeOb();
} else
return new SomeOtherOb();
}
#Override
public Class<? extends SomeInterface> getObjectType() {
if(/*Somecondition*/) {
return SomeOb.class;
} else
return SomeOtherOb.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
In the applicationContext.xml i have the tag mentioned.
When i run the webserver i run into an error like
No unique bean of type [com.xxxx.xxxx.SomeInterface] is defined: expected single matching bean but found 3: [xxxx, xxxxxxx, xxxxFactory]
Can anyone please help me to resolve this issue. If i am not doing this right please direct me to do this the right way.
Thanks and appreciate any help,
jjk
Thanks for the suggestion. I was able to solve the problem with help from a colleague. What i was doing wrong
I had the implementation of the SomeInterface with #Service. So this was picked up by the spring scanner and added to the bean.
During trial and error i removed the #Component annotation from by FactoryBean implementation.
After making these changes it worked like a charm.
return true from isSingleton() if you only need one implementation of the bean for a given instance of your application
But I question your design.
I would always use properties files to switch out implementations like this. I once had to implement CAPTCHA integration for a site. We were prototyping with the JCaptcah and ReCAPTCHA APIs. I created a new interface that contained just the functionality we needed and then created implementations for both APIs. Using a placeholders in the Spring configuration file and Maven profiles, we could switch out the implementation class at compile time or deployment time, for example, mvn jetty:run -DcaptchaImpl=recaptcha or -DcaptchaImpl=jcaptcha.
Without knowing the task that you want to accomplish, it's hard to provide more advice.
I am trying to add some Unit Testing to some of our companies code. Yes, I know it should already be there, but not everyone seems to have the same view of unit testing that I do.
However, I have come against a bit of a stopper for me. Admittedly, my Java, Spring and Unit Testing knowledge are not all that they should be. My problem is this though:
I have added a unit test to my code, which tests a class. This class includes a bean which has scope="request", and when it tries to instantiate the bean it throws an exception:
java.lang.IllegalStateException: No Scope registered for scope 'request'
I believe this is because I don't have a HttpServletRequest object, but I don't know how to create a mock one of these and also I don't know how, once created, to add this Mock Object to the unit test so that it resolves this problem.
Below is a cut down version of the code involved, which I believe includes all of the details that are part of this problem.
How can I get this to work?
#Test
public void handleRequest() {
try {
Message<?> outMessage = (Message<?>) response.handleRequest(map);
} catch (Exception e) {
assertNotNull(e);
}
outMessage.getPayload().toString());
}
public class upddResponse extends AbstractResponseTransform {
#SuppressWarnings("unchecked")
public Message<?> handleRequest(Map<String, Message<?>> messages) throws Exception {
super.addEnvironmentDetails(serviceResponseDocument.getServiceResponse());
}
public abstract class AbstractResponseTransform implements ResponseTransform,
ApplicationContextAware {
private ApplicationContext applicationContext;
private MCSResponseAggregator mcsResponseAggregator;
public ServiceResponseType addEnvironmentDetails(ServiceResponseType serviceResponse) throws Exception {
try {
mcsResponseAggregator = (MCSResponseAggregator) applicationContext
.getBean("mcsResponseAggregator");
}
catch (Exception ex) {
}
}
}
public interface ResponseTransform extends Transform {
public Message<?> handleRequest(Map<String, Message<?>> messages)
throws Exception;
}
<bean id="mcsResponseAggregator" class="com.company.aggregator.MCSResponseAggregator" scope="request" />
You need a WebApplicationContext to handle beans with: scope="request"
I recommend to use stub objects with Spring integration tests and use EasyMock without Spring when you test a class isolated.
You can use mocks within the Spring Context:
but that will not solve your problem as it will not make Spring understand scope="request". You can create your own implementation of the request scope, but I'm getting the feeling that you're better off not going through all this trouble.
The easy way out would be to override your request scoped bean in a little test context. You're technically not testing the original context then, but you will be done a lot quicker.
Spring 3.2 comes with support for this. See "Spring MVC Test Framework"