Spring Cloud Config Client connecting to RabbitMQ - spring

I have been trying to set up a Spring Cloud Config Server/Client. I have been following a few different examples (1, 2). I have the client and server set up correctly and can successfully query localhost:8888/localhost:8080 to see the values in JSON format.
My question is whether Spring Boot will automatically detect these properties provided by Spring Cloud Config Server. For now I am just attempting to connect to a RabbitMQ instance on startup but have had no success despite not having any errors. It does not connect to Rabbit or create the queues/exchanges.
It works when I have an application.properties file locally with the following properties but I wish to get these setting through Spring Cloud Config from a GitHub repository.
spring.rabbitmq.host=178.61.47.---
spring.rabbitmq.port=5672
spring.rabbitmq.username=mqtt
spring.rabbitmq.password=mqtt
I have looked through the questions here/issues on GitHub but can't see anything relating to this.
Code for client class is below:
#EnableAutoConfiguration
#ComponentScan
#SpringBootApplication
public class ConfigRabbitApplication {
final static String queueName = "arduino-weather-queue";
#Autowired
RabbitTemplate rabbitTemplate;
#Bean
Queue queue() {
return new Queue(queueName, true);
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("arduino-weather");
}
#Bean
TopicExchange exchange() {
return new TopicExchange("arduino-iot-exchange", true, false);
}
public static void main(String[] args) {
SpringApplication.run(ConfigRabbitApplication.class, args);
}
}

No, spring boot client is not aware that you want to fetch configuration from config-server. That is probably loaded when specific class in on a classpath, thats why you have to add org.springframework.cloud:spring-cloud-starter-config dependency. Its well described here: http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html#_client_side_usage
In case config-server is not on localhost:8888 you will also have to add:
spring.cloud.config.uri: http://myconfigserver.com
to your bootstrap.yml file ( its same as application.yml, just loaded earlier ).

Related

Spring GCP Cloud SQL and GCP Runtime Config processed out of order

Spring Cloud SQL is initialized with an EnvironmentPostProcessor factory and GCP Runtime Config is initialized with a BootstrapConfiguration factory, in that order. But I have Cloud SQL properties configured in the Runtime Config, so it's a little awkward. I see that there is (or used to be) a Spring Boot starter for this (spring-cloud-gcp-starter-config), but it doesn't seem to be maintained anymore. Does anyone even use the Runtime Config service? Here's how I've worked around this:
public class RuntimeConfigEnvironmentPostProcessor implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try {
if (Arrays.stream(environment.getActiveProfiles()).noneMatch("local"::equals)) {
GcpConfigProperties gcpConfigProperties = new GcpConfigProperties();
gcpConfigProperties.setEnabled(true);
gcpConfigProperties.setName(environment.getProperty("spring.application.name"));
gcpConfigProperties.setProfile(environment.getActiveProfiles()[
environment.getActiveProfiles().length - 1]);
gcpConfigProperties.setProjectId(environment.getProperty("spring.cloud.gcp.config.project-id"));
PropertySourceLocator locator = new GoogleConfigPropertySourceLocator(
new DefaultGcpProjectIdProvider(),
GoogleCredentials::getApplicationDefault,
gcpConfigProperties);
environment.getPropertySources().addLast(
locator.locate(environment));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
And I registered it in a spring.factories file which is something I've never had to do before. I found a similar issue and someone has worked around it in a similar manner: Spring Cloud Config and Spring Cloud Vault order of initialization
Is there a more elegant way?

How to config max-delivery-attempts for Spring Boot with Embedded ActiveMQ Artemis?

I would like to config the max-delivery-attempts of dead letter as described in the manual.
I tried Spring Boot with embedded ActiveMQ Artemis JMS server, but cannot figure out how to set max-delivery-attempts value.
After debugging the Configuration instance I finally find the method. Here's the code:
#Configuration
public class RedeliveryConfiguration implements ArtemisConfigurationCustomizer {
#Override
public void customize(org.apache.activemq.artemis.core.config.Configuration configuration) {
Map<String, AddressSettings> addressesSettings = configuration.getAddressesSettings();
// # is the catch all address or default address
addressesSettings.get("#").setMaxDeliveryAttempts(2);
}
}

Standalone wiremock server as spring boot application

I am trying to make a mock service as a spring boot application.
Can I use standalone mock server inside a spring boot application?
When I tried to run a mock server on any port inside the spring boot application it throws the "Address already bound exception"
Is there a way to over come that so that I can have a mockservice running as a spring boot docker container and just configure the urls I want to mock.
Basically, you would want to avoid any starter that brings up a container/server. Initially, I have only these two dependencies: com.github.tomakehurst:wiremock-jre8:2.31.0 and org.springframework.boot:spring-boot-starter-json. Optionally, confirm you don't want to run any server(s): spring.main.web-application-type: none.
Finally, declare a config file to setup WireMock:
#Configuration
#AllArgsConstructor
#EnableConfigurationProperties(WireMockConfig.ApplicationProps.class)
public class WireMockConfig {
private final ApplicationProps props;
#Bean
public WireMockServer mockServer() {
return new WireMockServer(WireMockConfiguration.options().port(8081));
}
}
...and a listener to start the server:
#Component
#AllArgsConstructor
public class ApplicationListener {
private final WireMockServer server;
#EventListener
public void onApplicationEvent(final ApplicationReadyEvent event) {
server.start();
}
}

spring boot actuator connect jmx programmatically

I'd like to use the shutdown endpoint of my Spring Boot 2.0.1 application from the command line. For that I have only added the spring-boot-starter-actuator to my Gradle file and enabled the shutdown endpoint in the configuration.
I also created a very simple tool that tries to connect via JMX to the running application.
Snippet:
String url = "service:jmx:rmi:///jndi/rmi://127.0.01:<which port?>/jmxrmi";
JMXServiceURL serviceUrl = new JMXServiceURL(url);
JMXConnectorFactory.connect(serviceUrl, null); <-- KAPOW!
JMX is working because I can use jconsole to connect locally. I just have no clue how to do it programmatically.
Any other attempts to explicitly set a port as mentioned here didn't work. Any hints?
It's probably easier to enable jolokia rather than using RMI; then you can simply
curl http://localhost:8080/actuator/jolokia/exec/org.springframework.boot:type=Admin,name=SpringApplication/shutdown
EDIT
If you prefer to use RMI, refer to the Spring Framework JMX Documentation.
Server app:
#SpringBootApplication
public class So50392589Application {
public static void main(String[] args) {
SpringApplication.run(So50392589Application.class, args);
}
#Bean
public RmiRegistryFactoryBean rmi() {
RmiRegistryFactoryBean rmi = new RmiRegistryFactoryBean();
rmi.setPort(1099);
return rmi;
}
#Bean
public ConnectorServerFactoryBean server() throws Exception {
ConnectorServerFactoryBean fb = new ConnectorServerFactoryBean();
fb.setObjectName("connector:name=rmi");
fb.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector");
return fb;
}
}
Client app:
#SpringBootApplication
public class JmxClient {
public static void main(String[] args) {
new SpringApplicationBuilder(JmxClient.class)
.web(WebApplicationType.NONE)
.run(args);
}
#Bean
public ApplicationRunner runner(MBeanServerConnection jmxConnector) {
return args -> {
jmxConnector.invoke(new ObjectName("org.springframework.boot:type=Admin,name=SpringApplication"),
"shutdown", new Object[0], new String[0]);
};
}
#Bean
public MBeanServerConnectionFactoryBean jmxConnector() throws Exception {
MBeanServerConnectionFactoryBean jmx = new MBeanServerConnectionFactoryBean();
jmx.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector");
return jmx;
}
}
There is a much simpler approach if you do not need to connect to the app remotely using the jcmd tool introduced in Java SE 7, and the Attach API introduced in Java SE 6.
I've written a blog post that explains this in detail. It's too big to simply copy-paste here, following is the link to the relevant section.
https://blog.asarkar.com/technical/grpc-kubernetes-spring/#jmx
This is not a duplicate answer because when the question was asked, this answer didn't exist. This answer has already been tailored to this question; let's not get trigger happy mods.

Spring 4.1 #JmsListener configuration

I would like to use the new annotations and features provided in Spring 4.1 for an application that needs a JMS listener.
I've carefully read the notes in the Spring 4.1 JMS improvements post but I continue to miss the relationship between #JmsListener and maybe the DestinationResolver and how I would setup the application to indicate the proper Destination or Endpoint.
Here is the suggested use of #JmsListener
#Component
public class MyService {
#JmsListener(containerFactory = "myContainerFactory", destination = "myQueue")
public void processOrder(String data) { ... }
}
Now, I can't use this in my actual code because the "myQueue" needs to be read from a configuration file using Environment.getProperty().
I can setup an appropriate myContainerFactory with a DestinationResolver but mostly, it seems you would just use DynamicDestinationResolver if you don't need JNDI to lookup a queue in an app server and didn't need to do some custom reply logic. I'm simply trying to understand how Spring wants me to indicate the name of the queue in a parameterized fashion using the #JmsListener annotation.
Further down the blog post, I find a reference to this Configurer:
#Configuration
#EnableJms
public class AppConfig implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setDefaultContainerFactory(defaultContainerFactory());
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setDestination("anotherQueue");
endpoint.setMessageListener(message -> {
// processing
});
registrar.registerEndpoint(endpoint);
}
Now, this makes some amount of sense and I could see where this would allow me to set a Destination at runtime from some external string, but this seems to be in conflict with using #JmsListener as it appears to be overriding the annotation in favor of endpoint.setMessageListener in the code above.
Any tips on how to specify the appropriate queue name using #JmsListener?
Also note that depending on use case you can already parameterize using properties file per environment and PropertySourcesPlaceholderConfigurer
#JmsListener(destinations = "${some.key}")
As per https://jira.spring.io/browse/SPR-12289
In case people are using #JmsListener with spring boot, you do not have to configure PropertySourcesPlaceholderConfigurer. It work's out the box
Sample:
class
#JmsListener(destination = "${spring.activemq.queue.name}")
public void receiveEntityMessage(final TextMessage message) {
// process stuff
}
}
application.properties
spring.activemq.queue.name=some.weird.queue.name.that.does.not.exist
Spring boot output
[26-Aug;15:07:53.475]-[INFO ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[931 ]-Successfully refreshed JMS Connection
[26-Aug;15:07:58.589]-[WARN ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[880 ]-Setup of JMS message listener invoker failed for destination 'some.weird.queue.name.that.does.not.exist' - trying to recover. Cause: User user is not authorized to read from some.weird.queue.name.that.does.not.exist
[26-Aug;15:07:59.787]-[INFO ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[931 ]-Successfully refreshed JMS Connection
[26-Aug;15:08:04.881]-[WARN ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[880 ]-Setup of JMS message listener invoker failed for destination 'some.weird.queue.name.that.does.not.exist' - trying to recover. Cause: User user is not authorized to read from some.weird.queue.name.that.does.not.exist
This proves that #JmsListener is able to pickup property values from application.properties without actually setting up any explicit PropertySourcesPlaceholderConfigurer
I hope this helps!
You could eventually do that right now but it's a bit convoluted. You can set a custom JmsListenerEndpointRegistry using JmsListenerConfigurer
#Configuration
#EnableJms
public class AppConfig implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setEndpointRegistry(customRegistry());
}
}
and then override the registerListenerContainer method, something like
public void registerListenerContainer(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
// resolve destination according to whatever -> resolvedDestination
((AbstractJmsListenerEndpoint)endpoint).setDestination(resolvedDestination);
super.registerListenerContainer(endpoint, factory);
}
But we could do better. Please watch/vote for SPR-12280

Resources