related question
I am sending message from REST API to SI message channel, which delegate to a sending message adapter. The message adapter send a message to TCP client. nothing needs to be return/response. My TCP client successfully received the message but the MVC controller throw an exception
Controller code
#Autowired
MessageChannel invokeChannel;
#RequestMapping(value="/invoke/{payload}")
public ResponseEntity<String> sayHello(#PathVariable String payload)
{
//trigger gateway to send a message
MessagingTemplate template = new MessagingTemplate();
template.send(invokeChannel, new GenericMessage<String>(payload));
return new ResponseEntity<String>(payload, HttpStatus.OK);
}
The Exception
java.lang.IllegalArgumentException: No converter found for return
value of type: class java.lang.String
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:178)
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:80)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:860)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
I couldn't find where and what is causing this. my breakpoint at return new ResponseEntity(...) is not reached.
my webmvcconfig
#EnableWebMvc
#Configuration
#ComponentScan({ "helloworldmvc" })
public class WebappConfig extends WebMvcConfigurerAdapter {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
converters.add(new MongoMessageConverter());
}
}
//trigger gateway to send a message
You should be sure that your flow for the template.send(invokeChannel) is really one-way. No one gateway may wait for rely there. That is for your comment like:
my breakpoint at return new ResponseEntity(...) is not reached.
If you can't reach that row of code, try to debug exactly that AbstractMessageConverterMethodProces. And there is need to figure out why producibleMediaTypes after the code:
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, clazz, type);
if (value != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + clazz);
}
is empty. Maybe you request uses some Accept non-compatible with the String? Or your WebMvcConfigurer overrides List<HttpMessageConverter<?>> converters somehow without any String-awere converter?
Error message:
No converter found for return value of type: class java.lang.String
It says that the MVC doesn't know which converter use to return the REST response from this service. to use the default converter which are available in spring.
Please add an annotation #ResposeBody in the method signature.
Related
My Vaadin 14 application should receive emails in the background. If emails with a certain subject have been received, the user should be informed about this via PUSH message on the UI.
For the entire email handling I implemented the email / message handling from Spring integration and that works too. Two beans (IntegrationFlow and a ServiceActivator) are generated via #Configuration and #Bean annotation in the Spring Application Context like so:
#Configuration
public class EmailReceiver {
#Bean
public HeaderMapper<MimeMessage> mailHeaderMapper() {
return new DefaultMailHeaderMapper();
}
#Bean
public IntegrationFlow imapMailFlow() {
IntegrationFlow flow = IntegrationFlows
.from(Mail.imapInboundAdapter("imaps://user:pass#imap.ionos.de/INBOX")
.userFlag("testSIUserFlag")
.javaMailProperties(new Properties()),
e -> e.autoStartup(true)
.poller(p -> p.fixedDelay(5000)))
.transform(Mail.toStringTransformer())
.channel(MessageChannels.queue("imapChannel"))
.get();
return flow;
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(1000));
return pollerMetadata;
}
#Bean
#ServiceActivator(inputChannel = "imapChannel")
public MessageHandler processNewEmail() {
MessageHandler messageHandler = new MessageHandler() {
#Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
System.out.println("new email received");
}
};
return messageHandler;
}
}
See also here: https://docs.spring.io/spring-integration/docs/current/reference/html/mail.html#mail-java-dsl-configuration
With such a #Configuration annotated class, the emails are received in the background of the Vaadin app. Check.
But how can I integrate a callback into a Vaadin view in the method EmailReceiver.processNewEmail?
#Bean
#ServiceActivator(inputChannel = "imapChannel")
public MessageHandler processNewEmail(UI ui) {
This always throws an error at application start: Scope vaadin-ui is not active for the current thread; consider defining a scoped proxy for this bean.
There is the example for asynchronous updates with Vaadin https://vaadin.com/docs/v14/flow/advanced/tutorial-push-access.
In contrast to this, I have to create a #Bean for #ServiceActivator handling. As soon as that is the case, there is always the error There is no UI available. The UI scope is not active.
If I move the method processNewEmail() into a separate class I still cannot reference a Vaadin UI:
#MessageEndpoint
class EmailMessageHandler {
private UI ui;
public EmailMessageHandler(UI ui) {
this.ui = ui;
}
#Bean
#ServiceActivator(inputChannel = "imapChannel")
public MessageHandler processNewEmail() {
MessageHandler messageHandler = new MessageHandler() {
#Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
System.out.println("new email received" + message);
}
};
return messageHandler;
}
}
How can I combine Vaadin asynchronous handling and Spring-Integration Email/ServiceActivator processing?
The point is that your mail receiving functionality is singleton per your application. On the other hand you are going to have as many UIs as many users do HTTP requests to your application. So, you need to think about some intermediary to dump email and get them from there when UI request happens.
You already have that imapChannel as a QueueChannel. So, you can take it from your UI scoped code and call its receive() API to pull the next message. Only the problem that it is a queue: as long as one call receive(), the other won't see the same message. Probably this is OK for your so far, but better to think about something what could be treated as topic in messaging terms. A good candidate easy to use is a Reactor's Sinks.Many: https://projectreactor.io/docs/core/release/reference/#sinks
I wish to understand what does serviceactivator annotation do? Because I want to modify message when I got it through serviceactivator. For example I have seen, there is no message parameter I can control. Why handle can receive message, even I cannot see any message parameter passed in, what is the principle?
#Bean
#ServiceActivator(inputChannel="requests")
public MessageHandler jmsMessageHandler((ActiveMQConnectionFactory connectionFactory) {
JmsSendingMessageHandler handler = new JmsSendingMessageHandler(new
JmsTemplate(connectionFactory));
handler.setDestinationName("requests");
return handler;
}
I wish I can do
#Bean
#ServiceActivator(inputChannel="requests")
public MessageHandler jmsMessageHandler(Message message) {
String new_message = message.split();
}
The #ServiceActivator wraps a call to the consumer endpoint. In case of MessageHandler it is used as is and the message from the inputChannel is passed to it. But if your code is not based on the MessageHandler, but is a simple POJO method invocation, then everything is based on the signature of your method. In the end that POJO method call is wrapped to the MethodInvokingMessageHandler.
In your case it must be something like this:
#ServiceActivator(inputChannel="requests", outputChannel="toJms")
public String jmsMessageHandler(Message message) {
return message.split();
}
So, no #Bean, because we deal only with POJO method invocation. The message is something incoming from request message and return String is going to become a payload from output message to processed somewhere downstream on the toJms channel.
See more info in the Reference Manual: https://docs.spring.io/spring-integration/docs/current/reference/html/#annotations
I'm trying to set a custom message converter for my Spring Integration Kafka message handler (yes, I know I can supply serializer configsāI'm trying to do something a little different).
I have the following:
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
final KafkaTemplate<String, String> kafkaTemplate = new KafkaTemplate<>(producerFactory());
kafkaTemplate.setMessageConverter(new MessagingMessageConverter() {
#Override
public ProducerRecord<?, ?> fromMessage(final Message<?> message, final String s) {
LOGGER.info("fromMessage({}, {})", message, s);
return super.fromMessage(message, s);
}
});
return kafkaTemplate;
}
#Bean
#ServiceActivator(inputChannel = "kafkaMessageChannel")
public MessageHandler kafkaMessageHandler() {
final KafkaProducerMessageHandler<String, String> handler = new KafkaProducerMessageHandler<>(kafkaTemplate());
handler.setTopicExpression(new LiteralExpression(getTopic()));
handler.setSendSuccessChannel(kafkaSuccessChannel());
return handler;
}
When a message is sent to kafkaMessageChannel, the handler sends it and the result shows up in kafkaSuccessChannel, but the RecordMessageConverter I set in the template was never called
The template message converter is only used when using template.send(Message<?>) which is not used by the outbound channel adapter.
The outbound adapter maps the headers itself using its header mapper; there is no conversion performed on the message payload.
What documentation leads you to believe the converter is used in this context?
I am using int:request-handler-advice-chain with my service activator. It is working correctly with org.springframework.retry.policy.SimpleRetryPolicy however I would like to use org.springframework.retry.policy.ExceptionClassifierRetryPolicy to allow for a different number of retries based on the exception thrown by the service activator.
The problem I am having is that by the time the exception gets to the ExceptionClassifierRetryPolicy it is a
org.springframework.integration.MessageHandlingException
Can anyone advise on the best approach for get the cause (i.e my exception) from the MessageHandlingException made available to the ExceptionClassifierRetryPolicy?
Solution thanks to Artem's suggestion below:
Create a subclass of SubclassClassifier that returns the cause in the case of MessagingException
public class MessagingCauseExtractingSubclassClassifier extends SubclassClassifier<Throwable, RetryPolicy> {
private static final Logger LOG = LoggerFactory.getLogger(MessagingCauseExtractingSubclassClassifier.class);
public MessagingCauseExtractingSubclassClassifier(final Map<Class<? extends Throwable>, RetryPolicy> policyMap, final RetryPolicy retryPolicy) {
super(policyMap, retryPolicy);
}
#Override
public RetryPolicy classify(final Throwable throwable) {
Throwable t = throwable;
if (t instanceof MessagingException) {
t = t.getCause();
LOG.debug("Throwable is instanceof MessagingException so classifying cause type: {}", t.getClass());
}
return super.classify(t);
}
}
Then a new ExceptionClassifierRetryPolicy subclass that uses the new classifier and policyMap
public class MessasgeCauseExtractingExceptionClassifierRetryPolicy extends ExceptionClassifierRetryPolicy {
#Override
public void setPolicyMap(final Map<Class<? extends Throwable>, RetryPolicy> policyMap) {
final MessagingCauseExtractingSubclassClassifier classifier = new MessagingCauseExtractingSubclassClassifier(
policyMap, new NeverRetryPolicy());
setExceptionClassifier(classifier);
}
}
Currently this won't support retying on MessagingException but this is fine for our use case. Otherwise works perfectly.
The BinaryExceptionClassifier has traverseCauses option to analize the whole StackTrace until the proper condition.
Exactly this option is with one of SimpleRetryPolicy constructor:
public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
boolean traverseCauses) {
Please, take a look if that variant is feasible for you.
I've encounter some problem while applying a small library to send email using wildfly email resource
Idea with library is to provide singleton providing asynchronous method to send emails.
in short service looks like
#Singleton
public class MailService {
private static final String MIME_TYPE = "text/html; charset=utf-8";
private static final Logger LOG = Logger.getLogger(MailService.class.getName());
#Inject
private Session session;
#Asynchronous
public void sendEmail(final EmailModel email) {
try {
MimeMessage message = new MimeMessage(session);
if (email.normalRecipientsListIsEmpty()) {
throw new RuntimeException("need destination address.");
}
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email.getNormalRecipients()));
message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(email.getCCRecipients()));
message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(email.getBCCRecipients()));
message.setSubject(email.getSubject());
message.setContent(email.getContent(), MIME_TYPE);
Transport.send(message);
} catch (MessagingException e) {
throw new RuntimeException("Failed to sen email.", e);
}
}
}
Injected session is produced in project via #Produces annotation in Stateless service field.
While on windows everything works fine, however if deployed on wildfly running on linux, there is an timeout exception with message like "could not obtain a lock on method within 5000milis"
When i moved whole code to project, with no changes, everything started to work perfectly.
My question is, why is this happening? Is there a difference in implementation somewhere or in configuration? How can i fix that and move code back to library where it can be reused in other projects?