How to Add an Around Advice to RabbitListener - spring-amqp

Asked and answered in reponse to Set MDC properties only for rabbitmq events

Add a MethodInterceptor bean and wire it into the container factory:
#Bean
MethodInterceptor interceptor(AbstractRabbitListenerContainerFactory<AbstractMessageListenerContainer> factory) {
MethodInterceptor inter = invocation -> {
try {
// do something before
return invocation.proceed();
}
finally {
// do something after
}
};
factory.setAdviceChain(inter);
return inter;
}

Related

How to properly call methods returning future in Reactor

To prevent the XY problem, I'll start from the beginning:
I have a non-blocking SOAP client which I wrapped it to make the return type Mono<T> (By default it accepts callback. I can elaborate on this if needed).
Now I want to do (given ID):
1. Get the code by ID
2. Do something with the code
3. After that, get Foo and Bar and create FooBar
What I wrote was:
public class MyService {
private final MySoapClient soapClient;
public Mono<FooBarDto> doSomething(String id) {
return Mono.just(id)
.flatMap(soapClient::getCode) // returns Mono<String>
.flatMap(code ->
soapClient.doSomething(code) // returns Mono<Void>
.then(getFooBar(id, code))); // See this
}
private Mono<FooBarDto> getFooBar(String id, String code) {
return Mono.zip(
soapClient.getFoo(code), // returns Mono<Foo>
soapClient.getBar(code) // returns Mono<Bar>
).map(tuple2 -> toFooBarDto(id, tuple2));
}
private FooBarDto toFooBarDto(String id, Tuple2<Foo, Bar> tuple2) {
return FooBarDto.builder()/* set properties */.build();
}
}
Now the problem is, because methods of the SOAP client are not lazy (the moment you call them they start the process), the semantic of then won't work here. Meaning I want to get Foo and Bar when doSomething is done. They all start together.
I tried to change it fix it by changing then to flatMap, but made it even worse. The getFooBar never got called. (1. Can someone please explain why?).
So what I ended up doing was to wrap SOAP calls again to make them lazy:
public class MySoapClient {
private final AutoGeneratedSoapClient client;
Mono<Foo> getFoo(GetFooRequest request) {
return Mono.just(request).flatMap(this::doGetMsisdnByIccid);
}
private Mono<Foo> doGetFoo(GetFooRequest request) {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return Mono.fromFuture(handler.future);
}
private static class AsyncHandler<T> implements javax.xml.ws.AsyncHandler<T> {
private final CompletableFuture<T> future = new CompletableFuture<>();
#Override
public void handleResponse(Response<T> res) {
try {
future.complete(res.get());
} catch (Exception e) {
future.completeExceptionally(e);
}
}
}
}
Is there any better way to do it? Specifically:
2. Using CompeletableFuture and the callback.
3. Making methods lazy in the SOAP client.
I tried to change it fix it by changing then to flatMap, but made it
even worse. The getFooBar never got called. (1. Can someone please
explain why?)
I think a Mono<Void> always completes empty (or error), so subsequent flatMap is never called.
Using CompeletableFuture and the callback.
Making methods lazy in the SOAP client.
To make the call lazy you can do one of the followings:
1, You can use Mono.fromFuture which accepts a supplier:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.fromFuture(() -> {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return handler.future;
});
}
2, You can use Mono.defer:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.defer(() -> {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return Mono.fromFuture(handler.future);
});
}
3, You can get rid of CompletableFuture and use Mono.create instead, something like this:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.create(sink -> {
AsyncHandler<Foo> handler = response ->
{
try
{
sink.success(response.get());
} catch (Exception e)
{
sink.error(e);
}
};
client.getFoo(request, handler);
});
}
If you do any of these it will be safe to use then method and it will work as expected.

Vaadin: MouseDown/MouseUp and KeyDown/KeyUp evens

Is it possible to handle MouseDown/MouseUp and KeyDown/KeyUp evens with Vaadin? I've found forum thread with the same question and looks like the answer is no, but it was 5 years ago - I hope something changed with later releases. Still I can't find anything in API. Maybe there's some workaround for intercepting such evens?
Well, after couple of days I came up with the acceptable (for me) solution. Required component has to be wrapped with extension-interceptor (credits to #petey for an idea in the comments) with KeyDownHandler inside. But the trick is not to add to the component itself (because it can miss triggering), but to the RootPanel. So here's a working example.
Extension:
public class InterceptorExtension extends AbstractExtension {
private boolean shiftKeyDown;
public InterceptorExtension(Tree tree) {
super.extend(tree);
registerRpc((InterceptorExtensionServerRpc) state -> shiftKeyDown = state);
}
public boolean isShiftKeyDown() {
return shiftKeyDown;
}
}
ServerRpc:
public interface InterceptorExtensionServerRpc extends ServerRpc {
void setShiftKeyDown(boolean state);
}
Connector:
#Connect(InterceptorExtension.class)
public class InterceptorExtensionConnector extends AbstractExtensionConnector {
#Override
protected void extend(final ServerConnector target) {
final InterceptorExtensionServerRpc rpcProxy = getRpcProxy(InterceptorTreeExtensionServerRpc.class);
final RootPanel rootPanel = RootPanel.get();
rootPanel.addDomHandler(new KeyDownHandler() {
#Override
public void onKeyDown(KeyDownEvent event) {
if (event.isShiftKeyDown()) {
rpcProxy.setShiftKeyDown(true);
}
}
}, KeyDownEvent.getType());
rootPanel.addDomHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent event) {
if (!event.isShiftKeyDown()) {
rpcProxy.setShiftKeyDown(false);
}
}
}, KeyUpEvent.getType());
}
}
Then whenever you want you can get Shift-button state on the server-side via InterceptorExtension#isShiftKeyDown.

Resolution of the dependency failed

I have the following code:
CancellationPolicyService
using MyApp.Model.Models;
using Repository.Pattern.Repositories;
using Service.Pattern;
namespace MyApp.Service
{
public interface ICancellationPolicyService : IService<CancellationPolicy>
{
}
public class CancellationPolicyService : Service<CancellationPolicy>, ICancellationPolicyService
{
public CancellationPolicyService(IRepositoryAsync<CancellationPolicy> repository)
: base(repository)
{
}
}
}
Inside UnityConfig.cs:
.RegisterType<ICancellationPolicyService, CancellationPolicyService>()
In DataCacheService:
namespace MyApp.Service
{
public class DataCacheService
{
private ICancellationPolicyService CancellationPolicyService
{
get { return _container.Resolve<ICancellationPolicyService>(); }
}
public DataCacheService(IUnityContainer container)
{
_container = container;
MainCache = new MemoryCache("MainCache");
GetCachedItem(CacheKeys.CancellationPolicies);
}
public object GetCachedItem(CacheKeys CacheKeyName)
{
lock (_lock)
{
if (!MainCache.Contains(CacheKeyName.ToString()))
{
switch (CacheKeyName)
{
case CacheKeys.CancellationPolicies:
var cancellationpolicies = CancellationPolicyService.Queryable().ToList();
UpdateCache(CacheKeys.CancellationPolicies, cancellationpolicies);
break;
}
};
return MainCache[CacheKeyName.ToString()] as Object;
}
}
}
}
And when I call DataCacheService I get an error saying the following:
InvalidOperationException - The current type, Repository.Pattern.Repositories.IRepositoryAsync`1[MyApp.Model.Models.CancellationPolicy], is an interface and cannot be constructed. Are you missing a type mapping?
Do you have an idea, why that is? I would be thankful for any kind of hint.
It sounds like you haven't registered IRepositoryAsync<CancellationPolicy>. Add that registration to your unity registration as well.
Assuming that the implementation of IRepositoryAsync<CancellationPolicy> is CancellationPolicyRepository:
.RegisterType<IRepositoryAsync<CancellationPolicy>, CancellationPolicyRepository>()
Or someting like this if you have a generic repository.
.RegisterType<IRepositoryAsync<CancellationPolicy>, MyGenericRepository<CancellationPolicyRepository>>()
use this one:
RegisterType<yourInterface>().As<yourClass>().AsSelf();
it might work.

How to add multiple Simplemessagelistenercontainer dynamically through application.properties

Below is our program, we create multiple containers for different queue through property in application.properties. But now it is static, when add another property, we must change the code.
I want add containers dynamically. I investigate several solutions.
1.use BeanFactory.registerSingleton method, but it cannot receive lifecycle callback,so i'm not sure the container can shutdown gracefully.
2.use BeanFactoryPostRegistor, but it need build a BeanDefinition, i have no idea how can construct a BeanDefinition for SimpleMessageListenerContainer, because it will be created by SimpleMessageListenrContainerFactory.
Can anybody give me better solution both add beans dynamically and the SimpleMessageListenerContainer can be started and shutdown normally?
#Bean
#ConditionalOnProperty(name = "pmc.multiple.hypervisor.reply.routerkey.kvm")
public SimpleMessageListenerContainer kvmReplyQueueConsumer() {
return getSimpleMessageListenerContainer(environment
.getProperty("pmc.multiple.hypervisor.reply.routerkey.kvm"));
}
#Bean
#ConditionalOnProperty(name = "pmc.multiple.hypervisor.reply.routerkey.vmware")
public SimpleMessageListenerContainer vmwareReplyQueueConsumer() {
return getSimpleMessageListenerContainer(environment
.getProperty("pmc.multiple.hypervisor.reply.routerkey.vmware"));
}
#Bean
#ConditionalOnProperty(name = "pmc.multiple.hypervisor.reply.routerkey.powervc")
public SimpleMessageListenerContainer powervcReplyQueueConsumer() {
return getSimpleMessageListenerContainer(environment
.getProperty("pmc.multiple.hypervisor.reply.routerkey.powervc"));
}
#Autowired
private SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory;
private SimpleMessageListenerContainer getSimpleMessageListenerContainer(String queueName){
return simpleRabbitListenerContainerFactory.createContainerInstance();
}
Take all properties you need (for example by regexp) and then register beans you want. There are 2 separate task there (1) how to get Spring properties (2) how register bean dynamically
1) To iterate over properties in 'Spring way':
#Autowired
Properties props;
....
for(Entry<Object, Object> e : props.entrySet()) {
if( /*some code to match*/ ){
//dispatch bean creation
}
}
2) you can either create beans dynamically by:
public MyClassPostRegister implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//create bean definition:
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MyBeanClass.class);
beanDefinition.setLazyInit(false);
beanDefinition.setAbstract(false);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setScope("prototype");
beanFactory.registerBeanDefinition("dynamicBean",beanDefinition);
}
Appendix after comment #GrapeBaBa:
Actually I use simpleRabbitListenerContainerFactory.createContainerInstance() to create container, so how to transform to use beanDefinition - please pay attention to lines marked with (!!!)
Create you own component
#Component
public class MyClassPostRegister implements BeanFactoryPostProcessor {
#Autowired
Properties props; //this gives you access to all properties
//following is example of filter by name
static final Pattern myInterestingProperties =
Pattern.compile("pmc\\.multiple\\.hypervisor\\.reply\\.routerkey\\..+");
Add post-process handler:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//iterate through properties
for(Entry<Object, Object> e : props.entrySet()) {
Matcher m = myInterestingProperties.matcher(e.key);
if( !m.matches() )
continue;
//create bean definition:
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(SimpleMessageListenerContainer.class);
beanDefinition.setLazyInit(false);
beanDefinition.setAbstract(false);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setScope("prototype");
//!!! Now specify name of factory method
beanDefinition.setFactoryMethodName("getSimpleMessageListenerContainer");
//!!! Now specify factory arguments:
ConstructorArgumentValues v = new ConstructorArgumentValues();
v.addGenericArgumentValue( e.getKey() ); //string
beanDefinition.getConstructorArgumentValues().add( v );
beanFactory.registerBeanDefinition("dynamicBean",beanDefinition);
}
}

override setter with call to inherited setter

I am a little bit confused: can I override a setter / getter but still use the super setter/getter? If yes - how?
Use case:
class A {
void set value(num a) {
// do something smart here
}
}
class B extends A {
void set value(num a) {
// call parent setter and then do something even smarter
}
}
If this is not possible how can one still preserve the API but expand the logic in the new class. The users of the code already use instance.value = ... so I do not want to change it to method call is possible.
Please help:)
You can access to parent with super. :
class B extends A {
void set value(num a) {
super.value = a;
}
}
Only need call super.value = a
class A {
void set value(String value) {
print(value.toUpperCase());
}
}
class B extends A {
void set value(String value) {
super.value = value;
print(value.toLowerCase());
}
}
void main() {
B b = new B();
b.value = "Hello World";
}

Resources