spring-amqp unable to use custom MessagingMessageListenerAdapter in SimpleMessageListenerContainerFactory - spring-amqp

In spring-amqp 2.0.3.RELEASE module it's no possible to use custom MessagingMessageListenerAdapter in SimpleMessageListenerContainerFactory.
Even thought registration own bean we stuck at highest one, where last object instance just hard created thought "new MethodRabbitListenerEndpoint" at org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor#processAmqpListener.
Maybe spring developers could add some producer registration to SimpleMessageListenerContainerFactory like "smlcf.setMessageListenerAdapterCreator"

I think what you are asking can be done via a RabbitListenerAnnotationBeanPostProcessor extension:
#Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static RabbitListenerAnnotationBeanPostProcessor myRabbitListenerAnnotationBeanPostProcessor() {
return new RabbitListenerAnnotationBeanPostProcessor() {
#Override
protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListener, Object bean,
Object adminTarget, String beanName) {
super.processListener(new MyMethodRabbitListenerEndpoint(), rabbitListener, proxy, adminTarget, beanName);
}
};
}
But what you are saying about retry for the reply really makes sense and we definitely should consider to let to inject a RabbitTemplate into the RabbitListenerContainerFactory.
Feel free to raise a JIRA on the matter.

I have created a story for adding retry functionality for ReplyTo https://jira.spring.io/browse/AMQP-825

Related

How to use custom processor on spring cloud data flow?

Here is the Stream I intend to implement:
It is supposed to read records from jdbc, transform to json and write on another database thru jdbc.
For this I have implemented (using the new functional approach):
#SpringBootApplication
public class StreamAppApplication {
private static ObjectMapper objectMapper;
public static void main(String[] args) {
SimpleModule module = new SimpleModule();
module.addSerializer(new ResultSetSerializer());
objectMapper = new ObjectMapper().registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(module);
SpringApplication.run(StreamAppApplication.class, args);
}
#Bean
public Function<ResultSet, String> recordToJson() {
return value -> {
try {
return objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new IllegalStateException("Falha conversão json", e);
}
};
}
}
On the application.properties
spring.cloud.stream.function.definition=recordToJson
Then I have imported it on the web UI as app of type TRANSFORM. It appeared on the UI with the transform classification and no parameters.
How do I use it?
You may want to review and follow the function-bindings recipe from the Microsite to get an understanding of what needs explicitly configured.
From what I can tell, you're likely missing the binding configuration for how your custom processor needs to consume and produce to the relevant channels.
Perhaps even repeat the samples from the recipe on your environment to get an understanding of how it comes together. With that then, you will be able to adapt your custom processor in the same data pipeline to validate it.

Fetch message details in Spring RecoveryCallback

I'm publishing messages into RabbitMQ and I would like to track the errors when RabbitMQ is down, for this I added one RetryTemplate with the recovery callback, but the recovery callback only provides this method getLastThrowable() and I'm not sure how to provide the details of the messages that failed when RabbitMQ is down. (as per documentation "The RecoveryCallback is somewhat limited in that the retry context only contains the
lastThrowable field. For more sophisticated use cases, you should use an external
RetryTemplate so that you can convey additional information to the RecoveryCallback via
the context’s attributes") but I don't know how to do that, if anyone could help me with one example that will be awesome.
Rabbit Template
public RabbitTemplate rabbitMqTemplate(RecoveryCallback publisherRecoveryCallback) {
RabbitTemplate r = new RabbitTemplate(rabbitConnectionFactory);
r.setExchange(exchangeName);
r.setRoutingKey(routingKey);
r.setConnectionFactory(rabbitConnectionFactory);
r.setMessageConverter(jsonMessageConverter());
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500);
backOffPolicy.setMultiplier(10.0);
backOffPolicy.setMaxInterval(10000);
retryTemplate.setBackOffPolicy(backOffPolicy);
r.setRetryTemplate(retryTemplate);
r.setRecoveryCallback(publisherRecoveryCallback);
return r;
}
Recovery Callback
#Component
public class PublisherRecoveryCallback implements RecoveryCallback<AssortmentEvent> {
#Override
public AssortmentEvent recover(RetryContext context) throws Exception {
log.error("Error publising event",context.getLastThrowable());
//how to get message details here??
return null;
}
}
AMQP Outbound Adapter
return IntegrationFlows.from("eventsChannel")
.split()
.handle(Amqp.outboundAdapter(rabbitMqTemplate)
.exchangeName(exchangeName)
.confirmCorrelationExpression("payload")
.confirmAckChannel(ackChannel)
.confirmNackChannel(nackChannel)
)
.get();
The isn't possible because the function RabbitTemplate.execute() is already not aware about message you send, because it may be performed from any other method, where we might not have messages to deal:
return this.retryTemplate.execute(
(RetryCallback<T, Exception>) context -> RabbitTemplate.this.doExecute(action, connectionFactory),
(RecoveryCallback<T>) this.recoveryCallback);
What I suggest you to do is like storing message to the ThreadLocal before send and get it from there from your custom RecoveryCallback.

Spring Integration Security with REST service example

I am implementing Spring Integration for REST services. I am following XPadro's githib example - https://github.com/xpadro/spring-integration.
I have created simple read, write and update operations.
Examples taken from int-http-dsl project.
I want to implement spring-security with oath2. I am taking reference from http://docs.spring.io/spring-integration/reference/html/security.html.
I am not able to connect both together. Because below is how they map a request
#Bean
public IntegrationFlow httpGetFlow() {
return IntegrationFlows.from(httpGetGate()).channel("httpGetChannel").handle("personEndpoint", "get").get();
}
#Bean
public MessagingGatewaySupport httpGetGate() {
HttpRequestHandlingMessagingGateway handler = new HttpRequestHandlingMessagingGateway();
handler.setRequestMapping(createMapping(new HttpMethod[]{HttpMethod.GET}, "/persons/{personId}"));
handler.setPayloadExpression(parser().parseExpression("#pathVariables.personId"));
handler.setHeaderMapper(headerMapper());
return handler;
}
and below is how we can integrate security
#Bean
#SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_ADMIN")
public SubscribableChannel adminChannel() {
return new DirectChannel();
}
I am not able to find a way to create channels in first example so how to integrate that.
Am I going right direction or getting it all wrong?
Is there any better tutorials to handle spring-integration (http) with spring-security (using oauth)?
Spring Integration Java DSL allows to use external #Beans for message channels from the flow definition. So, your httpGetChannel may be declared and used like:
#Bean
#SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_ADMIN")
public SubscribableChannel httpGetChannel() {
return new DirectChannel();
}
#Bean
public IntegrationFlow httpGetFlow() {
return IntegrationFlows.from(httpGetGate())
.channel(httpGetChannel())
.handle("personEndpoint", "get")
.get();
}
Feel free to raise a GitHub issue to make in the Framework something more obvious directly from the DSL's .channel() definition: https://github.com/spring-projects/spring-integration-java-dsl/issues

What is the scope of my Ninject injected ObjectContext in my custom MembershipProvider (using Request scope)?

I use Entity Framework 4 and ASP.NET MVC 3. I made a custom membership provider and use Ninject to inject an EFAccountRepository into it (Bound IAccountRepository to EFAccountRepository).
This account repository has an ObjectContext injected into it. I also use this repository (and others) in my controllers. For this reason when I bound IContext to my ObjectContext, I set the scope to "per request" so the ObjectContext only lives in one request and is shared between the repositories.
I am sometimes get the following error when trying to log in:"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
I wonder how often the membership provider gets instantiated. I injected the repository into the membership provider by marking the repository property with [Inject] and calling Kernel.Inject in the Application_Start function in the global.asax file.
If the provider gets instantiated more than once I would have to inject again I suppose. However, I don't get a null pointer exception, so I don't think that's it.
Update 1
Here's some code:
MyNinjectModule.cs
public override void Load()
{
Bind<IMyContext>().To<MyObjectContext>().InRequestScope();
// put bindings here
Bind<IAccountRepository>().To<EFAccountRepository>
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
var kernel = new StandardKernel(new MyNinjectModule());
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(kernel));
kernel.Inject(Membership.Provider);
}
MyMembershipProvider.cs
[Inject]
public IAccountRepository accountRepository { get; set; }
public override bool ValidateUser(string username, string password)
{
// I get the exception here.
return (from a in accountRepository.Accounts
where a.UserName == username
&& a.Password == password
select true).SingleOrDefault();
}
EFAccountRepository.cs
private readonly IMyContext context;
public EFAccountRepository(IMyContext context)
{
this.context = context;
}
public IQueryable<Account> Accounts
{
get { return context.Accounts; }
}
MyObjectContext.cs
public class MyObjectContext : ObjectContext, IMyContext
{
public IObjectSet<Account> Accounts { get; private set; }
public FlorenceObjectContext()
: this("name=DomainModelContainer")
{
}
public FlorenceObjectContext(string connectionString)
: base(connectionString, "DomainModelContainer")
{
Accounts = CreateObjectSet<Account>();
}
}
PS: I'm always open to comments on my code in general ;).
The exception says that you are incorrectly handling disposing of your context. Somewhere you call context.Dispose (or have context in using) but after that you want to use context again which is not possible because context is already disposed. If you are using per request context you must dispose context only once at the end of request processing (when you are sure that no code will use the context).
You didn't specify a scope for your EFAccountRepository binding so it defaults to .InTransientScope(). This means a new instance of the object will be created each time you resolve the IAccountRepository [refer to https://github.com/ninject/ninject/wiki/Object-Scopes ].
Also, transient scope objects
are automatically garbage collected as soon as there are no references to them [Ninject doesn't cache them]
are not automatically disposed by anyone
In contrast, you bound MyObjectContext to IObjectContext .InRequestScope(). This means it will be reused when you are in the same HTTP request handling operation.
Also, a request scope object
won't be garbage collected until your http request is done
might be automatically disposed once the HTTP request is done, if it's IDisposable. [Not sure precisely when, but from other questions I have seen I suspect it depends on the version of Ninject]
Now, ObjectContext is IDisposable, so it seems reasonable to conclude that
an object reference to the IObjectContext exists, and you are using the IObjectContext outside of the HTTP request which it was created in.
Ninject has automatically disposed of it, since the HTTP request has completed.
In order to solve the issue, you need to figure out why your object context object reference is surviving so long, and consider either eliminating the long-livedness... or removing its dependency on short-lived (request-scoped) objects.
[note clearly the question already has an accepted answer, but I think the accepted answer was kind of hard to understand.]

Best way of having Service communicate errors to Controller

When the Service layer is only executing a task (checking if Id exists, sending an email, etc.), what is the best way for it to let the controller know if there were any errors?
Two solutions I can think of:
Always passing in an extra "broken rules" parameter by reference to the methods in the Service layer which it would update if there were any error.
Have the Service raise an exception and having the controller do a try/catch.
Are either one of these two approaches recommended? If not, what approach could I take to have the Service layer let the controller know what something went wrong (such as invalid parameter)?
Your service should collection all the broken rules and after that throw the "BrokenRuleException". Your controller will catch the "BrokenRuleException" and then use the brokenrules to update the user interface.
I created interface:
public interface IModelStateWrapper
{
void AddModelError(string name, string error);
}
Then I created implementation for every controller:
public class ControllerModelStateWrapper : IModelStateWrapper
{
private ModelStateDictionary _dictionary;
public ControllerModelStateWrapper(ModelStateDictionary dictionary)
{
_dictionary = dictionary;
}
public void AddModelError(string name, string error)
{
if (_dictionary[name] == null)
_dictionary.Add(name, new ModelState());
_dictionary[name].Errors.Add(error);
}
}
Every service implements:
public interface IModelWrapperService
{
IModelStateWrapper ModelWrapper {get;set;}
}
And then I set it in Controller:
public UserController(IUserService service)
{
_service.ModelWrapper = new ControllerModelStateWrapper(ModelState);
}
IModelStateWrapper is not the best name, because this interface can work not only with Controller.ModelState. Works pretty ok. You can easily replace IModelStateWrapper with mock or other implementation in your service tests. This solution also automatically sets ModelState as invalid.
I think that throwing the BrokenRuleException is a good choice.
Personally, I don't like to put state in a service, it's often a singleton (performed by a DI container), and only has other singletons collaborators (in my case, domain objects).

Resources