I'am trying to send a simple message using "spring cloud stream" to the rabbitmq. Basically code looks like this:
#EnableBinding(Source.class)
#SpringBootApplication
public class SourceApplication {
public static void main(String[] args) {
SpringApplication.run(SourceApplication.class, args);
}
#Autowired Source source;
#PostConstruct
public void init() {
source.send(MessageBuilder.withPayload("payload").build());
}
}
then I get this error message:
org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'unknown.channel.name'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=******, headers={id=c60dd5be-6576-99d5-fd1b-b1cb94c191c1, timestamp=1488651422892}]
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:93)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
However, if I add some delay, before sending a message (just second or few), it works ok. My question is: how can I wait before spring completely initialize message channels and then send a message?
#PostConstruct is triggered too early (when the configuration bean is created, but before the context is started and the binding takes place). What you want is to trigger the sending of the message once the context is completely initialized, or at least after the output channels are bound.
You have a few options, all relying on the creation of an additional bean:
To use the SmartLifecycle support from Spring (make sure that isAutoStartup returns true by default and the phase is zero - the default - so that the bean is started after outputs are bound).
Use an ApplicationListener for ContextRefreshedEvent.
Since this is a Spring Boot application you can use an ApplicationRunner bean (which gets invoked after the context has been created).
You might look into Spring's Task Execution and Scheduling features.
In particular, it sounds like you want something like what section 34.4 covers.
Also, I spotted this answer to a similar question.
Related
Trying to register hystrix concurrency strategy fails after migrating to Spring-boot-2.0 with spring actuator enabled with java.lang.IllegalStateException stating that "Another strategy was already registered" . This is although I have not used registerConcurrencyStrategy anywhere else in my code.
I'd like to register concurrency strategy to carry-forward Log MDC context so that I'm able to log both within and outside the Hystrix wrapped method equally well, which includes thread-locals. And this used to work perfectly in spring-boot-1.5
After having migrated to spring-boot 2.0 (from 1.5), the HystrixPlugins.getInstance().registerConcurrencyStrategy(this); fails with IllegalStateException
As per https://github.com/Netflix/Hystrix/issues/1057, this issue can come if either (a) Any other code flow would have registered its own or default ConcurrencyStrategy before this is invoked (b) any call would have come via Hystrix before this is invoked
Since the above invocation is within the constructor of a class which is annotated with #Component, this should get invoked ideally before any method call happens (except initialization of other beans, including their constructors).
We even tried moving this code inside the SpringBoot Application Class's main method before invoking the SpringApplication.run(MyApplication.class, args); but that also didn't work
#Component
public class ContextCopyHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final String EVENT = "HystrixConcurrencyStrategy";
private static final String ACTION = "ContextCopy";
public ContextCopyHystrixConcurrencyStrategy(Logger logger, LoggerUtil defaultLoggerUtil) {
try {
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
} catch (IllegalStateException e) {
defaultLoggerUtil.logEvents(logger, Level.WARN, e.getMessage(), EVENT, ACTION, "", "Race condition! Could not register strategy. HystrixConcurrencyStrategy is already initialized.");
}
Expected: My registering should have happened before any other code and registering should have been successful
Actual: My registering fails with IllegalStateException
How do I make sure that my registering happens well before any other registering (which is not present in my code, but may be inside some of the libraries that I may be transitively using)
By default, Spring boot 2 accuator registers Hystrix Metric Binder beans which reset already set HystrixConcurrencyStrategy and sets HystrixConcurrencyStrategyDefault.
So, disabling that bean by
management.metrics.binders.hystrix.enabled=false
would help not resetting your custom HystrixConcurrencyStrategy
We took a close look at my maven .m2 directory classes and looked for registerConcurrencyStrategy inside all the classes in all the jars. And we found that
io.micrometer.core.instrument.binder.hystrix
was internally registering the HystrixConcurrencyStrategy with the default one.
And upon further research we found that setting the following property in application.properties:
management.metrics.binders.hystrix.enabled=false disabled the Hystrix Metrics Binder (I'm actually not sure what it does though!) and then things worked
I was using spring-boot-starter-parent v2.5.3 with Spring Cloud version 2020.0.3.
I had to manually include version for spring-cloud-starter-netflix-hystrix. I was getting "Another strategy was already registered" exception when starting my microservice. I included the
management.metrics.binders.hystrix.enabled=false
in the application.properties file and this issue got resolved.
This question already has answers here:
Running code after Spring Boot starts
(18 answers)
Closed 4 years ago.
I am building an application using Java Spring where I would like to run some environment setup code before my application starts handling requests. In this particular example, I'm using PayPal Rest SDK and I would like to set up some notification webhooks for my application. For obvious reasons I don't want to have an endpoint to call to set up the webhooks after the application is started, so putting it in my controller is probably not a good idea, and I need some Spring configuration information to set it up so I can't put it in main(). I'm ok with (in fact I'd even prefer) the application crashing if the webhooks fail to be created, if that's a constraint that needs to be considered.
What's a good way to do this?
Thanks.
and I need some Spring configuration information to set it up so I can't put it in main()
The above statement is not true. You can access your Spring configuration in a main. Consider the following example.
#SpringBootApplication
public class Main {
#Autowire
private MyService service;
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(InterviewHqApplication.class, args);
ctx.getBean(Main.class).setup();
}
private void setup() {
service.doStuff();
}
}
In this example, the setup() method is called after the application context has loaded.
There are actually several ways to do what you are attempting. Spring boot also supports using ApplicationRunner and CommandLineRunner, which both call a run method after the application context has been loaded an alternative to what I have shown above. You can also listen for an ApplicationReadyEvent and you could call #PostConstruct do perform some specific configuration on a bean after it's initialized.
It is my understanding that when you use Spring Cloud's RefreshScope annotation, a Proxy to the data is injected, and the proxy is automatically updated if the backing information is changed. Unfortunately, I need to find a way to be alerted when that refresh occurs, so that my code can re-read the data from the refresh-scoped bean.
Simple example: A scheduled task whose schedule is stored in Cloud Config. Unless you wait until the next execution of the task (which could take a while) or regularly poll the configuration (which seems wasteful), there's no way to know if the configuration has changed.
EnvironmentChangeEvent is fired when there's a change in Environment. In terms of Spring Cloud Config it means it's triggered when /env actuator endpoint is called.
RefreshScopeRefreshedEvent is fired when refresh of #RefreshScope beans has been initiated, e.g. /refresh actuator endpoint is called.
That means that you need to register ApplicationListener<RefreshScopeRefreshedEvent> like that:
#Configuration
public class AppConfig {
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Your code goes here...
}
}
When the refresh occurs EnvironmentChangeEvent would be raised in your config client, as the documentation states:
The application will listen for an EnvironmentChangedEvent and react
to the change in a couple of standard ways (additional
ApplicationListeners can be added as #Beans by the user in the normal
way).
So, you can define your event listener for this event:
public class YourEventListener implements ApplicationListener<EnvironmentChangeEvent> {
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
// do stuff
}
}
I think an approach can be to annotate with #RefreshScope all your bean that have properties externalized by the configuration and annotated within #Value ( "${your.prop.key}" ) annotation.
These properties are updated when they changed on configuration.
More specifically, after the refresh of properties and application context under scope RefreshScope, an event RefreshScopeRefreshedEvent is triggered. You can have a listener for this given the understanding that the properties has finished updates (you can be sure to capture updated values only).
I have created this Camel routes
from("direct:pageExtraction")
.bean(PageManager.class, "setProperties(*, ${headers})")
.filter().method(PageManager.class, "exists").to("seda:pagePostProcessing").end()
.to("seda:pageImposition");
from("seda:pagePostProcessing")
.bean(PageManager.class, "extractThumbnail(*, ${headers})")
.bean(PageManager.class, "extractCMYKSeparation(*, ${headers})")
.bean(PageManager.class, "persist(*, ${headers})")
.bean(PageManager.class, "cleanUp(${headers})")
.to("seda:pageImposition");
from("seda:pageImposition")
.bean(PageManager.class, "extractImposition(*, ${headers})")
.to("seda:printQueue");
At the end, the seda:printQueue has no consumers, sending a message in a route like this apparently works fine. Now I want to introduce a new consumer after the routes have been initialized, I thought it would be possible to create a Spring bean programmatically and let Camel pick up the bean using the #Consume(uri="seda:printQueue") annotation, but as soon as I create the consumer Camel complains
org.apache.camel.RuntimeCamelException: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '4965d710-b5c7-41cf-97e9-a42bdfcea894' is defined]
Any thoughts?
[UPDATE]
I have traced back the error to the class where this new consumer is created, I'm instantiating the PrintQueue class and then integrating it to the Spring context using an AutowireCapableBeanFactory doing a factory.autowireBean(printQueueInstance) followed by factory.initializeBean(printQueueInstance, id) where id is the 4965d710-b5c7-41cf-97e9-a42bdfcea894 that appears in the exception above, so I think this has to be some kind of context scope problem, may be I am creating this bean in the main or web Spring context and it can't be access by the Camel context, is this possible?
Since this route is invoked synchronously via use of the "direct:" component, it does not appear to require "seda:" for asynchronous invocation of another bean. In this situation, it would appear simplest to invoke a bean with Camel's bean methods for the Java DSL. As an example shown in the Camel bean documentation at:
http://camel.apache.org/bean.html
I would simply do:
// Send message to the bean endpoint
// and invoke given method.
from("direct:start")
// do other stuff in your route
.beanRef("beanName", "methodName");
I am building a TCP Socket Server which uses Spring Framework (3.2.3.RELEASE) to tie things together.
Everything is configured using Java Config with Annotations. In the Java Config I have a #ComponentScan annotation to scan for components in my classpath.
When messages are received by the TCP Socket Server, I occasionally want to dispatch some events. For this I use Google Guice EventBus. I don't want the TCP Socket Server to know about the event receivers and vice versa, to keep things loosely coupled.
I have registered a EventBusRegisterBeanPostProcessor from the code supplied by Patrick Meade # http://pmeade.blogspot.no/2013/02/using-guava-eventbus-with-spring-part-2.html
This will scan any bean and register it to the EventBus.
To listen for events (subscribe), I just need to create a POJO which looks like the following:
#Component
public class PingMessageReceivedHandler {
static Logger logger = Logger.getLogger(PingMessageReceivedHandler.class);
#Subscribe
public void handleMessageReceivedEvent(MessageReceivedEvent messageReceivedEvent) {
logger.info(messageReceivedEvent.getMessage());
messageReceivedEvent.getChannel().writeAndFlush(new BaseMessage(false, "Pong", null, ""));
}
}
Now, here is the issue: Unless I, somewhere in a different service or component or whatever, place a dependency on PingMessageReceivedHandler (using for instance #Autowired), then the EventBusRegisterBeanPostProcessor will not be aware of the PingMessageReceivedHandler and as such not dispatch messages to it.
I know that Spring is aware of the existence of the PingMessageReceivedHandler as the logs state so:
2013-11-04 17:35:05,391 INFO [main] (DefaultListableBeanFactory.java:596) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#9695ed7: defining beans [[...],pingMessageReceivedHandler,[...],eventBusRegisterBeanPostProcessor]; root of factory hierarchy
Any ideas of what is causing this, and even more importantly, how I fix it?
Edit:
Context configuration:
#ComponentScan(value = "com.company.product")
#Configuration
public class DeviceManagerServerConfig {
#Bean
public static EventBusRegisterBeanPostProcessor eventBusRegisterBeanPostProcessor() {
return new EventBusRegisterBeanPostProcessor();
}
#Bean
public EventBus eventBus() {
return new EventBus("standard");
}
}
It is initialized using:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(DeviceManagerServerConfig.class);
context.refresh();
The issue was that I attempted to initialize a bean which has a blocking operation (the TCP Socket Server).
This stopped Spring from further bean initialization, and caused the issue in the Original post.
I.e:
#Component
#Profile("default")
public class SocketServerImpl implements SocketServer {
/**
* Instantiates a new socket server.
*/
public SocketServerImpl() {
/* Standard stuff */
}
#PostConstruct
#Override
public void start() {
/* This would block any further progressing */
}
}
My fix was pretty simple, I removed #PostConstruct from the code above, and in my public static void main, after I called context.refresh(), I called context.getBean(SocketServer.class).start().
Try adding
#Lazy(false)
to your handler bean.
I think it is declared lazy by default somehow and is therefore not loaded on startup without a dependency.