Handler method name in SpringAmqp interceptors - java

I use spring amqp with multi-method listeners, like this:
#RabbitListener(queues = PLATFORM_COMMAND_QUEUE)
#Component
public class PlatformListener {
#RabbitHandler
public Response<GetAllPlatformsResponse> getAllPlatforms(GetAllPlatforms command) {
...
return Response.ok(GetAllPlatformsResponse.create(allPlatforms));
}
#RabbitHandler
public Response<PlatformResponse> getPlatform(GetPlatformCommand command) {
...
return Response.ok(platformService.getPlatform(command));
}
}
And I wand add specific header with handler name (getAllPlatforms, getPlatform) for all response messages. For that, i try add setAfterReceivePostProcessors and setBeforeSendReplyPostProcessors, but they do not provide any information about handler methods.
factory.setBeforeSendReplyPostProcessors(message -> {
Method targetMethod = message.getMessageProperties().getTargetMethod();
assert targetMethod == null;
return message;
});
How can i get method name and add it to reply message header?

It's not currently possible; as the javadocs state, that property is only populated for method-level #RabbitListener.
/**
* The target method when using a method-level {#code #RabbitListener}.
* #return the method.
* #since 1.6
*/
public Method getTargetMethod() {
return this.targetMethod;
}
Given some changes to the architecture over the years, I think it should now be possible to populate this also for class-level listeners. Please open a new feature suggestion and I'll take a look at adding it.

Related

Spring Integration CompositeFileListFilter

I am using CompositeFileListFilter to add two filters. Firstly, I want to execute AbstractPersistentFileListFilter with the default implementation, thereby protecting it from duplication.
Second, I use my own implementation, which checks the database for the existence of a file, protecting me from duplicates in the event of a system restart
How can this approach be released? So that the default implementation of AbstractPersistentFileListFilter with the internal memory of the MetadataStore is executed first
The goal is to reduce database calls to check for the existence of a file. Perhaps there is a better approach to the solution. Thanks for any help!
FtpConfiguration.java
#Bean
CompositeFileListFilter<FTPFile> getCompositeFileListFilter() {
CompositeFileListFilter<FTPFile> compositeFileListFilter = new CompositeFileListFilter<>();
compositeFileListFilter.addFilters(new CustomFileFilter(messageRepo));
return compositeFileListFilter;
}
#Bean
public IntegrationFlow ftpIntegrationFlow() {
return IntegrationFlows.from(
Ftp.inboundStreamingAdapter(template())
.filter(getCompositeFileListFilter())
.remoteDirectory("."),
e -> e.poller(Pollers.fixedDelay(500).advice(advice())))
.transform(new StreamTransformer("UTF-8"))
.handle(messageService::unmarshall)
.get();
}
CustomFileFilter.java
#Component
#Log4j2
public class CustomFileFilter implements FileListFilter<FTPFile> {
private final MessageRepo messageRepo;
public CustomFileFilter(MessageRepo messageRepo) {
this.messageRepo = messageRepo;
}
#Override
public List<FTPFile> filterFiles(FTPFile[] files) {
return null;
}
#Override
public boolean accept(FTPFile file) {
String fileName = file.getName();
log.info("file filter get name: {}", fileName);
Integer fileCheck = messageRepo.checkExistsMessage(fileName);
log.info("fileCheck: {}", fileCheck);
return fileCheck != 1;
}
#Override
public boolean supportsSingleFileFiltering() {
return true;
}
}
Use the ChainFileListFilter instead:
/**
* The {#link CompositeFileListFilter} extension which chains the result
* of the previous filter to the next one. If a filter in the chain returns
* an empty list, the remaining filters are not invoked.
*
* #param <F> The type that will be filtered.
*
* #author Artem Bilan
* #author Gary Russell
* #author Cengis Kocaurlu
*
* #since 4.3.7
*
*/
public class ChainFileListFilter<F> extends CompositeFileListFilter<F> {
https://docs.spring.io/spring-integration/docs/current/reference/html/file.html#file-reading
Starting with version 4.3.7, a ChainFileListFilter (an extension of CompositeFileListFilter) has been introduced to allow scenarios when subsequent filters should only see the result of the previous filter. (With the CompositeFileListFilter, all filters see all the files, but it passes only files that have passed all filters). ...

ClassCastException in Spring IntegrationFlow Implementation

I currently have an IntegrationFlow implementation that utilizes a Service class to implement all desired functionality to be performed by the flow. Something like this...
#Service
public class FlowService {
public Message<String> removeLineFeeds(Message<String> message) {
return MessageBuilder
.withPayload(StringUtils.remove(message.getPayload(), StringUtils.LF))
.copyHeadersIfAbsent(message.getHeaders())
.build();
}
}
#Configuration
#EnableIntegration
public class FlowConfiguration {
#Autowired
private FlowService flowService;
#Bean
public IntegrationFlow flow() {
return IntegrationFlows
.from("inputChannel")
.transform(flowService, "removeLineFeeds")
.get();
}
}
The above implementation works exactly as desired but I was hoping to improve/modify the implementation to utilize the power of Java 8/Lambdas so that it looked something like this...
#Bean
public IntegrationFlow flow() {
return IntegrationFlows
.from("inputChannel")
.transform(flowService::removeLineFeeds)
.get();
}
Unfortunately, when implemented this way, the flow will throw a ClassCastException whenever it processes a message. I have tried a few of the different proposed solutions that exist online currently but none of them seem to do the trick. I am encountering a similar issue regardless of the IntegrationFlow method used (transform, filter, etc.).
What needs to be changed with the current implementation to allow the use of flowService::removeLineFeeds within the IntegrationFlow methods?
EDIT: PER ARTEM'S RESPONSE
It appears a simple converter in the IntegrationFlow did the trick. My current implementation seemed to be passing the message as a Message<byte[]> instead of the Message<String> I was expecting. See Artem's full response below for more details.
#Bean
public IntegrationFlow flow() {
return IntegrationFlows
.from("inputChannel")
.convert(String.class)
.transform(flowService::removeLineFeeds)
.get();
}
The point is that lambda must correspond to some functional interface.
In case of transform() it is a GenericTransformer<S, T>. Indeed your Message<String> removeLineFeeds(Message<String> message) satisfies such a contract. And it would work well if you deal with only payload:
public String removeLineFeeds(String message) {
return StringUtils.remove(message.getPayload(), StringUtils.LF);
}
Just because when all the generic information from the target implementation is erased at runtime we can't guess you would like to deal with the whole Message<?>, so the framework only propagates to your lambda only a payload. That how your String cannot be cast to Message, therefore a ClassCastException.
To fix the problem and mock Java generics system we suggest an overloaded method with an explicit expected type:
/**
* Populate the {#link MessageTransformingHandler} instance for the provided
* {#link GenericTransformer} for the specific {#code payloadType} to convert at
* runtime.
* #param payloadType the {#link Class} for expected payload type. It can also be
* {#code Message.class} if you wish to access the entire message in the transformer.
* Conversion to this type will be attempted, if necessary.
* #param genericTransformer the {#link GenericTransformer} to populate.
* #param <P> the payload type - 'transform from' or {#code Message.class}.
* #param <T> the target type - 'transform to'.
* #return the current {#link BaseIntegrationFlowDefinition}.
* #see MethodInvokingTransformer
* #see LambdaMessageProcessor
*/
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer) {
So, your configuration should look like this:
.transform(Message.class, flowService::removeLineFeeds)
This way we say the framework that we would like to get a whole message for our function to process.
Anyway I'd prefer the first variant just with a payload: the framework will take care for you about coping request headers into a reply message.
See more info in Docs: https://docs.spring.io/spring-integration/reference/html/dsl.html#java-dsl-class-cast

How to escape only message instead of all row in log4j?

I have the following PatternLayout:
public class EscapedEnhancedPatternLayout extends EnhancedPatternLayout {
#Override
public String format(LoggingEvent event) {
return StringEscapeUtils.escapeJava(super.format(event));
}
}
but this escapes full logging row.
I want to have something like this but only for message.
but LoggingEvent class has not setMessage and setRenderedMessage methods.
And I don't see copy constructor at LoggingEvent. If LoggingEvent had copy constructor I can inherited from LoggingEvent and override methods mentioned below.
Please advise me how to solve my issue.
You're right, there is no LoggingEvent(LoggingEvent other) constructor, but you can pass the event's values to the LoggingEvent constructor in your format method like this:
#Override
public String format(LoggingEvent event) {
Object msgObj = event.getMessage();
LoggingEvent newEvent = new LoggingEvent(
event.getFQNOfLoggerClass(),
event.getLogger(), event.getTimeStamp(),
event.getLevel(),
StringEscapeUtils.escapeJava(msgObj != null ? msgObj.toString() : null),
event.getThreadName(),
event.getThrowableInformation(),
event.getNDC(),
event.getLocationInformation(),
event.getProperties());
return super.format(newEvent);
}
This will create a new LoggingEvent from the old one with all values set. The StringEscapeUtils.escapeJava method can now modify the the message without affecting the other properties and you can still use super.format
An alternative approach could be to create a customized LogEventFactory. A LogEventFactory is used to generate LogEvents. Applications may replace the standard LogEventFactory by setting the value of the system property Log4jLogEventFactory to the name of the custom LogEventFactory class.
The method that will be called to create a instance of LogEvent is as follows:
LogEvent createEvent(String loggerName,
org.apache.logging.log4j.Marker marker,
String fqcn,
org.apache.logging.log4j.Level level,
org.apache.logging.log4j.message.Message data,
List<Property> properties,
Throwable t)
and DefaultLogEventFactory is the basic implementation for log4j. In your case you could extend the basic implementation and escape the message after which you call the default implementation. It would become something like this:
public class MyCustomLogEventFactory extends DefaultLogEventFactory {
/**
* Creates a log event.
*
* #param loggerName The name of the Logger.
* #param marker An optional Marker.
* #param fqcn The fully qualified class name of the caller.
* #param level The event Level.
* #param data The Message.
* #param properties Properties to be added to the log event.
* #param t An optional Throwable.
* #return The LogEvent.
*/
#Override
public LogEvent createEvent(final String loggerName, final Marker marker,
final String fqcn, final Level level, final Message data,
final List<Property> properties, final Throwable t) {
super.createEvent(loggerName, marker, fqcn, level, StringEscapeUtils.escapeJava(data != null ? data.toString() : null), properties, t);
}
}

variable / constructor in an interface

I was going through the picasso source code and came across this chunk in lines 80-94:
public interface RequestTransformer {
/**
* Transform a request before it is submitted to be processed.
*
* #return The original request or a new request to replace it. Must not be null.
*/
Request transformRequest(Request request);
/** A {#link RequestTransformer} which returns the original request. */
RequestTransformer IDENTITY = new RequestTransformer() {
#Override public Request transformRequest(Request request) {
return request;
}
};
}
From my understanding, it's somewhat declaring a variable in the interface with a static constructor. Can someone explain what is that code supposed to be doing? I read through a similar post regarding constructors in interfaces (Constructor in an Interface?) but I still don't see why this case does not apply there.
Thanks
This actually is not a variable. This is constant with anonymous implementation. Within interface it is compiled to:
public interface RequestTransformer {
Request transformRequest(Request request);
public static final RequestTransformer IDENTITY = new RequestTransformer() {
#Override
public Request transformRequest(Request request) {
return request;
}
};
}
And this is a bad practice (to have implementation within interface) :)

Avoiding instanceof when checking a message type

I have the following situation where a client class executes different behavior based on the type of message it receives. I'm wondering if there is a better way of doing this since I don't like the instanceof and the if statements.
One thing I thought of doing was pulling the methods out of the client class and putting them into the messages. I would put a method like process() in the IMessage interface and then put the message specific behavior in each of the concrete message types. This would make the client simple because it would just call message.process() rather than checking types. However, the only problem with this is that the behavior contained in the conditionals has to do with operations on data contained within the Client class. Thus, if I did implement a process method in the concrete message classes I would have to pass it the client and I don't know if this really makes sense either.
public class Client {
messageReceived(IMessage message) {
if(message instanceof concreteMessageA) {
concreteMessageA msg = (concreteMessageA)message;
//do concreteMessageA operations
}
}
if (message instanceof concreteMessageB) {
concreteMessageb msg = (concreteMessageB)message;
//do concreteMessageB operations
}
}
The simple way to avoid instanceof testing is to dispatch polymorphicly; e.g.
public class Client {
void messageReceived(IMessage message) {
message.doOperations(this);
}
}
where each message class defines an appropriate doOperations(Client client) method.
EDIT: second solution which better matches the requirements.
An alternative that replaces a sequence of 'instanceof' tests with a switch statement is:
public class Client {
void messageReceived(IMessage message) {
switch (message.getMessageType()) {
case TYPE_A:
// process type A
break;
case TYPE_B:
...
}
}
}
Each IMessage class needs to define an int getMessageType() method to return the appropriate code. Enums work just as well ints, and are more more elegant, IMO.
One option here is a handler chain. You have a chain of handlers, each of which can handle a message (if applicable) and then consume it, meaning it won't be passed further down the chain. First you define the Handler interface:
public interface Handler {
void handle(IMessage msg);
}
And then the handler chain logic looks like:
List<Handler> handlers = //...
for (Handler h : handlers) {
if (!e.isConsumed()) h.handle(e);
}
Then each handler can decide to handle / consume an event:
public class MessageAHandler implements Handler {
public void handle(IMessage msg) {
if (msg instanceof MessageA) {
//process message
//consume event
msg.consume();
}
}
}
Of course, this doesn't get rid of the instanceofs - but it does mean you don't have a huge if-elseif-else-if-instanceof block, which can be unreadable
What type of message system are you using?
Many have options to add a filter to the handlers based on message header or content. If this is supported, you simply create a handler with a filter based on message type, then your code is nice and clean without the need for instanceof or checking type (since the messaging system already checked it for you).
I know you can do this in JMS or the OSGi event service.
Since you are using JMS, you can basically do the following to register your listeners. This will create a listener for each unique message type.
String filterMsg1 = "JMSType='messageType1'";
String filterMsg2 = "JMSType='messageType2'";
// Create a receiver using this filter
Receiver receiverType1 = session.createReceiver(queue, filterMsg1);
Receiver receiverType2 = session.createReceiver(queue, filterMsg2);
receiverType1.setMessageHandler(messageType1Handler);
receiverType2.setMessageHandler(messageType2Handler);
Now each handler will receive the specific message type only (no instanceof or if-then), assuming of course that the sender sets the type via calls to setJMSType() on the outgoing message.
This method is built into message, but you can of course create your own header property and filter on that instead as well.
//Message.java
abstract class Message{
public abstract void doOperations();
}
//MessageA.java
class MessageA extends Message{
public void doOperations(){
//do concreteMessageA operations ;
}
}
//MessageB.java
class MessageB extends Message {
public void doOperations(){
//do concreteMessageB operations
}
}
//MessageExample.java
class MessageExample{
public static void main(String[] args) {
doSmth(new MessageA());
}
public static void doSmth(Message message) {
message.doOperations() ;
}
}
A Java 8 solution that uses double dispatch. Doesn't get rid of instanceof completely but does only require one check per message instead of an if-elseif chain.
public interface Message extends Consumer<Consumer<Message>> {};
public interface MessageA extends Message {
#Override
default void accept(Consumer<Message> consumer) {
if(consumer instanceof MessageAReceiver){
((MessageAReceiver)consumer).accept(this);
} else {
Message.super.accept(this);
}
}
}
public interface MessageAReceiver extends Consumer<Message>{
void accept(MessageA message);
}
With JMS 2.0 you can use:
consumer.receiveBody(String.class)
For more information you can refer here:

Categories