I'm using spring-integration-sftp and my goal is to push local file to SFTP (just that for now, without confirmation or anything else). My configuration is as follows:
#EnableIntegration
#IntegrationComponentScan
#Configuration
#Lazy
public class SftpConfiguration {
#Bean(name = "toSftpChannel")
public MessageChannel sftpMessageChannel() {
return new DirectChannel();
}
#Bean
public DefaultSftpSessionFactory sftpSessionFactory(
#Qualifier("sftpDestination") SftpPropertiesService sftpPropertiesService
) {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory();
factory.setHost(sftpPropertiesService.getServiceHost());
factory.setPort(22);
factory.setUser(sftpPropertiesService.getUsername());
factory.setPassword(sftpPropertiesService.getPassword());
factory.setAllowUnknownKeys(true);
return factory;
}
#Bean
public SftpRemoteFileTemplate sftpRemoteFileTemplate(DefaultSftpSessionFactory dssf,
#Value("${sftp.output.directory}") String outputDirectory) {
SftpRemoteFileTemplate template = new SftpRemoteFileTemplate(dssf);
template.setRemoteDirectoryExpression(new LiteralExpression(outputDirectory));
return template;
}
#Bean
#ServiceActivator(inputChannel = "toSftpChannel")
public MessageHandler handler(SftpRemoteFileTemplate sftpRemoteFileTemplate) {
SftpOutboundGateway gateway =
new SftpOutboundGateway(sftpRemoteFileTemplate, Command.PUT.getCommand(), "payload");
gateway.setFileExistsMode(FileExistsMode.FAIL);
return gateway;
}
#MessagingGateway
public interface OutputSftpGateway {
#Gateway(requestChannel = "toSftpChannel")
void sendToSftp(File file);
}
}
and sending is just
private final OutputSftpGateway outputSftpGateway;
...
outputSftpGateway.sendToSftp(file);
When I'm running my code I at first get
A bean definition with name 'toSftpChannel' exists, but failed to be created; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'toSftpChannel': Requested bean is currently in creation: Is there an unresolvable circular reference?
which is kind of expected with lazy init (though still will have to fix it), but at the second and subsequent runs I'm getting stuck with
Exception occurred during request processing. org.springframework.messaging.MessageDeliveryException. Dispatcher has no subscribers for channel 'application.toSftpChannel'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
I'll honestly admit that I'm new with Spring messaging magic, so the cause is probably very stupid but can someone give me a hint why this is happening and how can I fix it?
The #Lazy may have some effect on those beans initialization. Consider to divide your configuration logic to extract only those beans which cannot live with #Lazy. And don't apply it for those Spring Integration components.
Related
I'm using Spring Boot 1.5.9 version and Feign and Fallback.
I get a strange error when calling a single service through the Feign client.
Help, please.
My Feign config
#Configuration
#Import(HeaderConverter.class)
public class SecuritiesFeignConfig {
private final HeaderConverter headerConverter;
public SecuritiesFeignConfig(HeaderConverter headerConverter) {
this.headerConverter = headerConverter;
}
#Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> headerConverter.getCallContextHeaders().forEach(requestTemplate::header);
}
#Bean
public Decoder feignDecoder() {
HttpMessageConverter<?> jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);
return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
}
private ObjectMapper customObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
}
and my main config class in app
#Configuration
#Import(FiltersConfig.class)
public class frameworkConfig {
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MSACallContextImpl callContext() {
return new MSACallContextImpl();
}
#Bean
public CallContextCreator callContextCreator() {
return new CallContextCreator();
}
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ContextHeadersBuilder contextHeadersBuilder(MSACallContextImpl callContextImpl) {
return new ContextHeadersBuilder(callContextImpl);
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
But when I try to make a service call through the Feign client, I get the following error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.contextHeadersBuilder': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I found the solution for the current problem.
Need to add into the application properties file
hystrix:
command:
default:
execution:
timeout:
enabled: false
isolation:
thread:
timeoutInMilliseconds: 6000
strategy: SEMAPHORE
As it seems like Bean creation is not taking place and the #Autowired is not able to create the OBJECT so try
#Service Annotation will be missing as it worked for me when i put #Service Annotation in Class level
Problem: I have a application which has simple SimpleRabbitListenerContainerFactory bean defined.
There is a component created which has generic implementaion for RabbitListenerAroundAdvice which is meant to be applicable to any application which has SimpleRabbitListenerContainerFactory bean and is added as dependency.
One way to set the advicechain is in the application which means every application relying on this advice need to undergo a change.
I want to set the advice chain of the SimpleRabbitListenerContainerFactory in the RabbitListenerAroundAdvice component for which I need the SimpleRabbitListenerContainerFactory bean defined in the application. How do I get this bean in order to set advicechain?
Example: Bean as in application
#Bean
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory, #Value("${queue.consumers}") int concurrentConsumer) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrentConsumers(concurrentConsumer);
factory.setMaxConcurrentConsumers(concurrentConsumer);
factory.setPrefetchCount(1);
return factory;
}
RabbitListenerAroundAdvice component as below:
//set the advice of the SimpleRabbitListenerContainerFactory so that the message is intercepted.
public class RabbitListenerAroundAdvice implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//business logic to to check before handling the message
final Object result = methodInvocation.proceed();
return result;
}
}
I'm having to change from simply using #JmsListener to setting up my listeners dynamically in order to allow my user to configure the application choosing which queue to read to.
I have tried follow the Spring JMS Documentation for Programmatic Endpoint Registration but there is one aspect it does not cover: how to set the ListenerContainerFactory I want to use for my listener.
I have tried the following:
#Configuration
#EnableJms
public class JmsConfig implements JmsListenerConfigurer {
#Autowired
private JmsListenerEndpointRegistry registry;
#Overide
public void configureJmsListeners(JmsListenerEndpointRegistrar register) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myJmsEndpoint");
endpoint.setDestination("TestQueue");
endpoint.setupListenerContainer(registry.getListenerContainer("myContainerFactory"))
endpoint.setMessageListener( message -> {
// handle
});
register.registerEndpoint(endpoint)
}
#Bean
public JmsListenerContainerFactory myContainerFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
// Other Connection and Container factories
}
but I'm receiving:
java.lang.IllegalArgumentException: Could not configure endpoint with the specified container 'null' Only JMS (org.springframework.jms.listener.AbstractMessageListenerContainer subclass) or JCA (org.springframework.jms.listener.endpoint.JmsMessageEndpointManager) are supported.
at org.springframework.jms.config.AbstractJmsListenerEndpoint$JcaEndpointConfigurer.configureEndpoint(AbstractJmsListenerEndpoint.java:188)
I'm guessing the null is because, at this phase, Spring still doesn't have the beans created (right?).
What is the right approach here to make this work?
Thanks!
I've got java spring boot configuration like this:
#Bean
public SubscribableChannel channel(MessageHandler handler) {
PublishSubscribeChannel channel = new PublishSubscribeChannel(Executors.newFixedThreadPool(2));
channel.subscribe(handler);
return channel;
}
My handler code:
#Service
public class SomeDataHandler implements MessageHandler {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.print(message.getPayload());
}
}
And some client code:
#Autowired
private SubscribableChannel channel
...
channel.send(...)
And it doesn't work. Just nothing happens.
But this configuration works fine for me:
#Bean
public SubscribableChannel channel(MessageHandler handler) {
PublishSubscribeChannel channel = new PublishSubscribeChannel();
channel.subscribe(handler);
return channel;
}
So looks like I need to do something more to apply task executor to my channel. Any ideas? Thanks.
Spring integration version - 4.2.5
"doesn't work" is not much to go on - show your executor and handler bean configurations.
Turn on DEBUG logging - it should help you figure out what's happening.
You should NOT be subscribing to the channel in the bean declaration; the handler will be subscribed automatically by the framework (if properly configured).
EDIT
You are not really using the framework as it was designed; when using an executor, the dispatcher is replaced during bean creation - overriding your subscription. You are subscribing too early in the bean lifecycle.
While not necessary, it's generally better to subclass AbstractMessageHandler and implement handleMessageInternal - then configure it like this...
#ServiceActivator(inputChannel="channel")
#Bean
public MyAMHSubclass handler() {
return new MyAMHSubclass();
}
and remove the subscribe from the channel bean declaration.
The #ServiceActivator wraps the handler in a consumer which subscribes to the channel when the context is initialized.
This is a separate yet related question to my earlier post here: Safely Terminating a Spring JMS application
My JMS application that I have using spring boot processes everything correctly and shuts down with no errors. To get this to work I changed a bean from:
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(mqConnectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setConcurrency("1");
factory.setErrorHandler(errorHandler());
factory.setSessionTransacted(true);
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
return factory;
}
To:
#Bean
public DefaultMessageListenerContainer defaultMessageListenerContainer() {
DefaultMessageListenerContainer jmsListenerContainer = new DefaultMessageListenerContainer();
jmsListenerContainer.setConnectionFactory(mqConnectionFactory());
jmsListenerContainer.setDestinationResolver(destinationResolver());
jmsListenerContainer.setDestinationName(queueName);
jmsListenerContainer.setConcurrency("1");
jmsListenerContainer.setErrorHandler(errorHandler());
jmsListenerContainer.setSessionTransacted(true);
jmsListenerContainer.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsListenerContainer.setAutoStartup(false);
return jmsListenerContainer;
}
The problem with this is, I could have created just a "hotfix", as my knowledge about spring is little. the line in the changed bean jmsListenerContainer.setAutoStartup(false); was added when I stumbled upon this post: http://forum.spring.io/forum/spring-projects/integration/79176-illegalstateexception-no-message-listener-specified as without the autoStartup set to false I get this after every processed message in my logs:
java.lang.IllegalStateException: No message listener specified - see property 'messageListener'
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:691) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:315) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:253) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1150) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1142) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1039) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at java.lang.Thread.run(Unknown Source) [?:1.8.0_74]
Everything still is processed and shut down correctly, I just see this in my logs. Not sure if there is a conflict in my config file or not that may be the root of this. I just want to make sure the changes won't cause problems, even though everything currently works as intended with no errors.
Lastly here is my entire configuration class:
#Configuration
#EnableJms
public class MQConfig {
private static final Logger logger = LogManager.getLogger(MQConfig.class.getName());
#Value("${mq.hostName}")
String host;
#Value("${mq.port}")
Integer port;
#Value("${mq.queueManager}")
String queueManager;
#Value("${mq.queueName}")
String queueName;
#Value("${mq.channel}")
String channel;
#Autowired
Environment environment;
#Bean
public DefaultMessageListenerContainer defaultMessageListenerContainer() {
DefaultMessageListenerContainer jmsListenerContainer = new DefaultMessageListenerContainer();
jmsListenerContainer.setConnectionFactory(mqConnectionFactory());
jmsListenerContainer.setDestinationResolver(destinationResolver());
jmsListenerContainer.setDestinationName(queueName);
jmsListenerContainer.setConcurrency("1");
jmsListenerContainer.setErrorHandler(errorHandler());
jmsListenerContainer.setSessionTransacted(true);
jmsListenerContainer.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsListenerContainer.setAutoStartup(false);
return jmsListenerContainer;
}
#Bean
public MQConnectionFactory mqConnectionFactory() {
MQConnectionFactory mqConnectionFactory = new MQConnectionFactory();
try {
mqConnectionFactory.setHostName(host);
mqConnectionFactory.setPort(port);
mqConnectionFactory.setQueueManager(queueManager);
mqConnectionFactory.setTransportType(1);
} catch (JMSException ex) {
logger.error(ex.getStackTrace());
}
return mqConnectionFactory;
}
#Bean
public DynamicDestinationResolver destinationResolver() {
DynamicDestinationResolver destinationResolver = new DynamicDestinationResolver();
try {
Connection connection = mqConnectionFactory().createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destinationResolver.resolveDestinationName(session, queueName, false);
} catch (JMSException ex) {
logger.error(ex.getStackTrace());
}
return destinationResolver;
}
#Bean
public MQQueue mqQueue() {
MQQueue mqQueue = new MQQueue();
try {
mqQueue.setBaseQueueName(queueName);
mqQueue.setBaseQueueManagerName(queueManager);
} catch (JMSException ex) {
logger.error(ex.getStackTrace());
}
return mqQueue;
}
#Bean
public JmsErrorHandler errorHandler(){
return new JmsErrorHandler();
}
#Bean
public MQManager mqHoldManager(){
return new MQManager(host, port, queueName,
queueManager, channel);
}
#Bean
public MQInitializer mqInitializer(){
return new MQInitializer(environment);
}
}
Thoughts? Is this messy?
EDIT: JMS Listener
#Component
public class MQConsumer {
#Resource(name = "mqHoldManager")
private MQManager mqHoldManager;
#Resource(name = "defaultMessageListenerContainer")
private DefaultMessageListenerContainer listenerContainer;
#Autowired
MQInitializer mqInitializer; /* To ensure this bean executes before Listener Setup not necessarily for any particular usage*/
final Logger logger = LogManager.getLogger(MQConsumer.class.getName());
private static ReportManager reportManager = new ReportManager();
private static boolean isFirstQueue = true;
#JmsListener(destination = "${mq.queueName}")
public void processOrder(String message) throws Exception {...}
I am confused.
DefaultJmsListenerContainerFactory is for use with annotated POJO methods:
#JmsListener(...)
public void foo(String bar) {...}
The factory is used to create the listener container for the method.
With your replacement configuration, I don't see you ever configuring a message listener into the DefaultMessageListenerContainer.
Usually you would have container.setMessageListner(myListener) where myListener is a MessageListener, a SessionAwareMessageListener or a MessageListenerAdapter that wraps a POJO listener.
Setting autoStartup to false and never starting the container does nothing except add the container bean to the context.
I don't see how you can ever get any messages with that configuration.
EDIT
Are you using Spring Boot? If so, it will create a default jmsListenerContainerFactory for you - that is my best guess.
In which case, your stop code is not really stopping the actual container - just the "dummy" one that was never started.
I suggest you give your #JmsListener an id, #Autowire the JmsListenerEndpointRegistry and call registry.getListenerContainer("myListener").stop().
#JmsListener(id = "myListener", destination = "${mq.queueName}")