Why Spring Data r2dbc not detecting Transaction manager by method return type when using #Transactional annotation? - spring-transactions

In my project we need to use 2 transaction managers(for JPA and R2DBC),
both of them are initialized with auto-configuration(two beans with names transactionManager and connectionFactoryTransactionManager),
documentation(link) says that:
Methods returning a reactive type such as Publisher or Kotlin Flow (or a subtype of those) qualify for reactive transaction management.
But actually is not true, spring fails with exception message:
No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,connectionFactoryTransactionManager
When I analyzed the code, the main logic detecting which tx manager to use is located in TransactionAspectSupport.determineTransactionManager
but unfortunately this logic not detecting tx manager by methods returning type, as it says the documentation.
Well, as a workaround I can choose which tx manager to use explicitly in "#Transactional" annotation.
This logic(auto detection of which tx manager to use depending on method return value) is not implemented yet, or I'm doing something wrong?

Related

Multiple #ServiceActivator methods with the same inputChannel and different signature

I'm trying to implement an annotation driven event bus (e.g. Guava Event Bus) using spring integration.
I have a PublishSubscribeChannel where I publish my events and the idea is to use methods annotated with #ServiceActivator as event handlers.
Each method can have a different signature based on the event (payload) they need to handle.
What I noticed is that when an event is published, all instances of ServiceActivatingHandler created by the ServiceActivatorAnnotationPostProcessor are called and an exception for each method that has a signature that does not match the payload. E.g.
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E:(pos 8): Method call: Method handle(model.api.ServiceAvailableEvent) cannot be found on service.eai.TestServiceActivatorImpl2 type
Is there a way to define a #ServiceActivator method only for specific payload types?
That's correct, all the subscribers for the PublishSubscribeChannel accept the same message. And if there is no any chance to convert incoming payload into expected method argument type, we get that exception.
If you would like to filter unexpected types, you definitely have to use #Filter before your #ServiceActivator. In other words you do the same as now, but make your flow(s) a bit complex with front filters as subscribers to that PublishSubscribeChannel.
You even can rely on the existing PayloadTypeSelector:
#Bean
#Filter(inputChannel = "publishSubscribeChannel", outputChannel="service1")
public MessageSelector payloadTypeSelector() {
return new PayloadTypeSelector(...);
}
Or, yes, just simple POJO method which checks the payload type and marked with the same #Filter.
I guess your next question will be like: why doesn't #ServiceActivator ignore those types which aren't unsuitable for the target method?
Just don't mix concerns. Service Activator is for Message handling in the target business logic. For filtering and skipping we have a difefrent EI pattern - filter.

spring cache does work w/ nested method

I has one method to call another #Cacheable method like this:
public ItemDO findMethod2(long itemId) {
this.findMethod1(itemId);
...
}
#Cacheable(value = "Item", key="#itemId", unless="#result == null")
public ItemDO findMethod1(long itemId) {
...
}
The cache works well if I call the findMethod1() directly. However, when I call findMethod2() the the cache on findMethod1() is totally ignored.
Could it be the trick made by JVM which inline the findMethod1() into findMethod2()?
Does anyone come across similar issue?
Thanks!
It's no JVM trick, i.e. findMethod1() is not being inlined inside findMethod2() or anything of that nature.
The problem is your code is bypassing the "Proxy" that Spring is creating around your application class (containing findMethod1()) for the #Cacheable annotation.
Like Spring's Transactional annotations and underlying infrastructure, given an interface, by default Spring will create a JDK Dynamic Proxy (AOP style) to "intercept" the method call and apply the "advice" (as determined by the type of annotation, in this case, caching). However, once the target object is invoked from the interceptor (Proxy) acting on behalf of the target object to apply the advice, the Thread is now executing in the context of the target object so any subsequent method invocations from within the target object are occurring directly on the target object itself.
It looks a little something like this...
caller -> Proxy -> findMethod2() -> findMethod1()
Ideally what you want is this...
caller -> Proxy -> findMethod2() -> Proxy -> findMethod1()
However, the Thread is already executing in the context of the "target" object once inside findMethod2(), so you end up with the first call stack.
The Spring doc explains it better here.
The document goes on to point out solutions to this problem, the most favorable is refactoring your code to ensure the caller is going through the Proxy interceptor for the 2nd method invocation (i.e. findMethod1()).
I also gather another solution to this problem would be to use full-blown AspectJ, using a compiler and byte-code weaver during your application build process to modify the actual target object so that subsequent invocations from within the target object intercept and apply the advice accordingly.
See the Spring docs on the trade-offs between Spring AOP and full AspectJ, as well as how to use full AspectJ in your Spring applications.
Hope this helps.
Cheers!
Other solution I find handy is using #Resource and then invoking the target (method1 in your case) using that resource reference with https://stackoverflow.com/a/48867068/2488286

Why i can't put more than 1 setters of same argument type in class which implements genericHandler in dsl?

I have created a class implementing GenericHandler to use in .handle() method. I have setters for the class, but if i have more than 1 setter with same argument type, i am getting "Found Ambiguous parameter type".
Why there is such restriction?
That's just because ServiceActivatingHandler is based on the MessagingMethodInvokerHelper logic on background to determine the appropriate messaging method. And setters are candidate for that purpose.
So, if you really hae several of them with the same param type, we end up with ambiguity issue.
To fix your case, I suggest mark your Object handle(P payload, Map<String, Object> headers); implementation with #ServiceActivator.
From other side I agree that it is not so good as we expect from Framework. So, feel free to raise a JIRA issue on the matter and we will fix .handle() to be more strict and rely only on the handle() method from the GenericHandler implementation.
I faced the same problem while using Spring integration while using a service adaptor. Could not define multiple properties of type java.lang.String - I would get a IllegalArgumentException claiming "ambiguous parameters".
After finding no solution to the issue, decided to just create a class to encapsulate those properties, configure this as a bean, and then inject it into the spring-integration config.

CDI extension, altering processed type

Using Weld 1.1.13.Final in test with Arquillian....
Let's say I inject into a field something volatile. Something like a property subject to change that I want the bean owning the injection point to receive change events. Thought about creating a CDI extension.
Caught ProcessAnnotatedType event and looking for all fields that have an custom annotation on field injection points:
<T> void pat(#Observes ProcessAnnotatedType<T> event, BeanManager bm) {
final AnnotatedType<T> target = event.getAnnotatedType();
for (AnnotatedField<? super T> field : target.getFields())
if (field.isAnnotationPresent(Value.class)) { // ignore that I don't check #Inject here for the moment
CtClass wrapper = pool.get(target.getJavaClass().getName());
ConstPool cp = wrapper.getClassFile().getConstPool();
CtMethod m = CtNewMethod.make(....)
....
wrapper.addMethod(m);
event.setAnnotatedType(bm.createAnnotatedType(wrapper.toClass()));
}
}
Had even grabbed thereafter all the injection points for fields and replaced the underlying WeldField with a new Field corresponding the "wrapper" type. Otherwise bean validation fails.
But this only works for stuff setup during startup not when for example Arquillian uses the Bean Manager to initialize a class that injects one of my "wraps". Things fail since the Bean Resolver uses the Type as a hash key to find beans.
Basically I don't think I can "mask" a class that is annotated (made into a bean) by the CDI with an extra method to receive custom events. Would have been cool but a Type is a Type (i.e. no idea how to proxy or fake the equals/hashCode).
Got it. Turns out the compute value function (google extension) inside the TypeSafeBeanResolver resolver (at least the CDI Weld implementation) is smart. If I just extend the class:
CtClass wrapper = pool.makeClass(target.getJavaClass().getName()+"Proxy");
wrapper.setSuperclass(pool.get(target.getJavaClass().getName()));
.....
final AnnotatedType<T> other = bm.createAnnotatedType(wrapper
.toClass());
then everything works fine. Tested capturing an event in a bean. Will post the code on a Gist with a comment.

How to serialize class that derives from class decorated with DataContract(IsReference=true)?

I have class A that derives from System.Data.Objects.DataClasses.EntityObject.
When I try to serialize using
var a = new A();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(a.GetType());
serializer.WriteObject(Response.OutputStream, a);
I get error
TestController+A._Id' is not marked with OptionalFieldAttribute, thus indicating that it must be serialized. However, 'TestController+A' derives from a class marked with DataContractAttribute and an IsReference setting of 'True'. It is not possible to have required data members on IsReference classes. Either decorate 'TestController+A._Id' with OptionalFieldAttribute, or disable the IsReference setting on the appropriate parent class.
Even if I decorate the field with OptionalFieldAttribute I get
The type 'TestController+A' cannot be serialized to JSON because its IsReference setting is 'True'. The JSON format does not support references because there is no standardized format for representing references. To enable serialization, disable the IsReference setting on the type or an appropriate parent class of the type.
I cannot modify EntityObject class. I thought to create A_Bag class exactly as A class and fill it and serialize it instead of A, but I think there's more elegant way to do it.
Can you suggest how I can do it?
I think you can use a "data contract surrogate" here (used via the IDataContractSurrogate interface.)
The data contract surrogate is an advanced feature built upon the Data Contract model you're already using. It lets you do type customization and substitution in situations where you want to change how a type is serialized, deserialized, or (if you're dealing with XML) projected into schema.
In your case, the use of IDataContractSurrogate lets you do custom JSON serialization and deserialization on a per-type or per-object basis. An IDataContractSurrogate would provide the methods needed to substitute one type for another by the DataContractSJsonerializer during serialization and deserialization, and you may want to provide a different "special" intermediary type for your scenario.
Hope this helps!
JSON.Net supports serialization of objects marked with IsReference=true.
There is a detailed walkthrough here:
http://dotnet.learningtree.com/2012/04/03/working-with-the-entity-framework-and-the-web-api/

Resources