Spring boot - Messaging with RabbitMQ to listen message queue continuously - spring-boot

When I run the Messaging with RabbitMQ guide [I tried this demo application][1] , the listener only receive the message once (the runner send once and then exited). I hope the listener can continuously listen the message queue, how should I achieve it ?
the receiver code:
package hello;
import java.util.concurrent.CountDownLatch;
import org.springframework.stereotype.Component;
#Component
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
}
the runner code:
package hello;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class Runner implements CommandLineRunner {
private final RabbitTemplate rabbitTemplate;
private final Receiver receiver;
public Runner(Receiver receiver, RabbitTemplate rabbitTemplate) {
this.receiver = receiver;
this.rabbitTemplate = rabbitTemplate;
}
#Override
public void run(String... args) throws Exception {
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(Application.topicExchangeName, "foo.bar.baz", "====1===========Hello from RabbitMQ!");
rabbitTemplate.convertAndSend(Application.topicExchangeName, "foo.bar.baz", "====2===========Hello from RabbitMQ!");
rabbitTemplate.convertAndSend(Application.topicExchangeName, "foo.bar.baz", "=====3==========Hello from RabbitMQ!");
receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
}
}
the application code:
ackage hello;
import hello.test.TestListener;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
static final String topicExchangeName = "spring-boot-exchange";
public static final String queueName = "spring-boot";
#Bean
Queue queue() {
return new Queue(queueName, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(topicExchangeName);
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
return container;
}
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(Application.class, args).close();
}
}
the log
[opuser#iZ25fprd8a9Z java]$ java -jar gs-messaging-rabbitmq-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.1.RELEASE)
2019-01-10 09:40:53.846 INFO 412 --- [ main] hello.Application : Starting Application on iZ25fprd8a9Z with PID 412 (/data/workspace/test/java/gs-messaging-rabbitmq-0.1.0.jar started by opuser in /data/workspace/test/java)
2019-01-10 09:40:53.854 INFO 412 --- [ main] hello.Application : No active profile set, falling back to default profiles: default
2019-01-10 09:40:55.019 INFO 412 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration' of type [org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration$$EnhancerBySpringCGLIB$$870d6481] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-01-10 09:40:56.047 INFO 412 --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [10.171.135.109:5672]
2019-01-10 09:40:56.145 INFO 412 --- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#76f2b07d:0/SimpleConnection#49c43f4e [delegate=amqp://petmq#10.171.135.109:5672/, localPort= 41068]
2019-01-10 09:40:56.152 INFO 412 --- [ main] o.s.amqp.rabbit.core.RabbitAdmin : Auto-declaring a non-durable, auto-delete, or exclusive Queue (spring-boot) durable:false, auto-delete:false, exclusive:false. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
2019-01-10 09:40:56.244 INFO 412 --- [ main] hello.Application : Started Application in 3.116 seconds (JVM running for 4.001)
Sending message...
Received <====1===========Hello from RabbitMQ!>
Received <====2===========Hello from RabbitMQ!>
Received <=====3==========Hello from RabbitMQ!>
2019-01-10 09:40:56.269 INFO 412 --- [ main] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2019-01-10 09:40:57.267 INFO 412 --- [ main] o.s.a.r.l.SimpleMessageListenerContainer : Successfully waited for workers to finish.
2019-01-10 09:40:57.272 INFO 412 --- [ main] o.s.a.r.l.SimpleMessageListenerContainer : Shutdown ignored - container is not active already

If you add the dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Your application will not close out.

Related

How to configure Spring Boot 2 SASL OAUTHBEARER to connect to KAFKA AZURE EVENT HUB?

I am trying to connect a simple Spring Boot kafka Consumer to Azure Event Hub Kafka
I must authenticate to Azure Event Hub via Azure AD before polling or consuming message from topic
In my application.yaml
server:
port: 9000
spring:
kafka:
bootstrap-servers: xxxx.servicebus.windows.net:9093
properties:
sasl.jaas.config: org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required;
sasl.mechanism: OAUTHBEARER
security.protocol: SASL_SSL
request.timeout.ms: 60000
**sasl.login.callback.handler.class**: com.xxx.config.CustomAuthenticateCallbackHandler
consumer:
group-id: compass
properties:
spring.json:
use.type.headers: false
value.default.type: com.xxx.consumerMessage
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
topic:
name: global.xxx
I added this class to implement Kafka custom OAUTHBEARER to get and validate Token from Azure AD :
package com.xxxx..consumer.config;
//Copyright (c) Microsoft Corporation. All rights reserved.
//Licensed under the MIT License.
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeoutException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.AppConfigurationEntry;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.KafkaException;
import org.apache.kafka.common.security.auth.AuthenticateCallbackHandler;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerToken;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback;
import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ClientCredentialParameters;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IAuthenticationResult;
import com.microsoft.aad.msal4j.IClientCredential;
public class CustomAuthenticateCallbackHandler implements AuthenticateCallbackHandler {
final static ScheduledExecutorService EXECUTOR_SERVICE = Executors.newScheduledThreadPool(1);
private String authority;
private String appId;
private String appSecret;
private ConfidentialClientApplication aadClient;
private ClientCredentialParameters aadParameters;
#Override
public void configure(Map<String, ?> configs, String mechanism, List<AppConfigurationEntry> jaasConfigEntries) {
String bootstrapServer = Arrays.asList(configs.get(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG)).get(0).toString();
bootstrapServer = bootstrapServer.replaceAll("\\[|\\]", "");
URI uri = URI.create("https://" + bootstrapServer);
String sbUri = uri.getScheme() + "://" + uri.getHost();
this.aadParameters =
ClientCredentialParameters.builder(Collections.singleton(sbUri + "/.default"))
.build();
this.authority = "https://login.microsoftonline.com/xxxxxx/"; // replace <tenant-id> with your tenant id
this.appId = "xxxxxx"; // also called client id
this.appSecret = "xxxxx"; // also called client secret
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback: callbacks) {
if (callback instanceof OAuthBearerTokenCallback) {
try {
OAuthBearerToken token = getOAuthBearerToken();
OAuthBearerTokenCallback oauthCallback = (OAuthBearerTokenCallback) callback;
oauthCallback.token(token);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
} else {
throw new UnsupportedCallbackException(callback);
}
}
}
OAuthBearerToken getOAuthBearerToken() throws MalformedURLException, InterruptedException, ExecutionException, TimeoutException
{
if (this.aadClient == null) {
synchronized(this) {
if (this.aadClient == null) {
IClientCredential credential = ClientCredentialFactory.createFromSecret(this.appSecret);
this.aadClient = ConfidentialClientApplication.builder(this.appId, credential)
.authority(this.authority)
.build();
}
}
}
IAuthenticationResult authResult = this.aadClient.acquireToken(this.aadParameters).get();
System.out.println("TOKEN ACQUIRED");
return new OAuthBearerTokenImp(authResult.accessToken(), authResult.expiresOnDate());
}
public void close() throws KafkaException {
// NOOP
}
}
My doubts are :
Is this the best solution to do that with Spring , should I write by myself the class which implements AuthenticateCallbackHandler
If I change the **client Id ** constant to wrong value in CustomAuthenticateCallbackHandler.Java , in the console an exception is added :
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: com.microsoft.aad.msal4j.MsalServiceException: AADSTS700016: Application with identifier 'xxxxxx00f832e67A' was not found in the directory 'e4e1abd9-eac7-4a71-ab52-da5c998aa7ba'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
But after that, the console shows: Successfully logged in, is it normal to not authenticate and get Polling?
2021-07-07 15:00:07.924 INFO 16108 --- [ restartedMain] .o.i.e.ExpiringCredentialRefreshingLogin : ***Successfully logged in.***
2021-07-07 15:00:07.974 WARN 16108 --- [ restartedMain] o.a.k.clients.consumer.ConsumerConfig : The configuration 'spring.json.value.default.type' was supplied but isn't a known config.
2021-07-07 15:00:07.975 WARN 16108 --- [ restartedMain] o.a.k.clients.consumer.ConsumerConfig : The configuration 'spring.json.use.type.headers' was supplied but isn't a known config.
2021-07-07 15:00:07.976 INFO 16108 --- [ restartedMain] o.a.kafka.common.utils.AppInfoParser : Kafka version: 2.7.1
2021-07-07 15:00:07.976 INFO 16108 --- [ restartedMain] o.a.kafka.common.utils.AppInfoParser : Kafka commitId: xxx
2021-07-07 15:00:07.977 INFO 16108 --- [ restartedMain] o.a.kafka.common.utils.AppInfoParser : Kafka startTimeMs: 1625662807975
2021-07-07 15:00:07.978 INFO 16108 --- [ restartedMain] o.a.k.clients.consumer.KafkaConsumer : [Consumer clientId=consumer-xxxx-1, groupId=xxx] Subscribed to topic(s): global.finance.copa
2021-07-07 15:00:07.996 INFO 16108 --- [ restartedMain] c.l.c.consumer.KafkaOnAzureApplication : Started KafkaOnAzureApplication in 1.703 seconds (JVM running for 2.555)
2021-07-07 15:00:08.178 WARN 16108 --- [ntainer#0-0-C-1] org.apache.kafka.clients.NetworkClient : [Consumer clientId=consumer-xxx-1, groupId=xxxx] Connection to node -1 (xxx.servicebus.windows.net/xxxxx:9093) could not be established.
The SASL OUATHBEARER:

Why can't I log before starting a Spring Boot Application?

I am trying to log something before my Spring Boot application starts. Here is a snippet below. I am using Lombok and Log4J2 and I have done the spring-boot-starter-logging exclusion + added spring-boot-starter-log4j2. I was wondering how to make it work and why the present code does not work.
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.log4j.Log4j2;
#SpringBootApplication
#Log4j2
public class DemoApplication {
public static void main(String[] args) {
log.info("Does not work");
SpringApplication.run(DemoApplication.class, args);
log.info("Works");
}
}
Result in console:
2020-11-30 19:45:32.667 INFO 25132 --- [ main] c.e.d.DemoApplication : Starting DemoApplication using Java 11.0.1 on *** with PID 25132 (**** started by **** in ****)
2020-11-30 19:45:32.702 INFO 25132 --- [ main] c.e.d.DemoApplication : No active profile set, falling back to default profiles: default
2020-11-30 19:45:33.921 INFO 25132 --- [ main] c.e.d.DemoApplication : Started DemoApplication in 2.008 seconds (JVM running for 3.103)
2020-11-30 19:45:33.927 INFO 25132 --- [ main] c.e.d.DemoApplication : Works
Updated:
However, as shown below, using Slf4j with default LogBack works, why not Log4j2?
(Log4j2 with Slf4j still does not)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
log.info("Does not work");
SpringApplication.run(DemoApplication.class, args);
log.info("Works");
}
}
20:04:25.945 [main] INFO com.example.demo.DemoApplication - Does not work
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.0)
2020-11-30 20:04:26.463 INFO 33284 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 11.0.1 on **** with PID 33284 (**** started by **** in ****)
2020-11-30 20:04:26.465 INFO 33284 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2020-11-30 20:04:27.234 INFO 33284 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.17 seconds (JVM running for 2.109)
2020-11-30 20:04:27.242 INFO 33284 --- [ main] com.example.demo.DemoApplication : Works
In the end I was able to answer my own question by stepping through the code + some research. Sharing my results:
In Log4j2, the default root filter level, when not providing a config (or before Spring instantiates the Log4j2-spring config) is ERROR, therefore isEnabled returns false and the logger does not print to the console the first time. However, once instantiated by Spring, the level becomes INFO and therefore the messages is printed to the console as the log level is now superior or equal to INFO level. QED

Java Spring Boot Rest API 400 Bad Request

I am trying to make a async Rest API with Spring Boot.
The Rest API receives http requests that have a json body with one value "zahl", then it extracts that value, opens a socket and sends a tcp request with that number to a loadbalacer, that divides the Requests on different servers.
They all make the same and calculate wheter the given number is a prime or not.
If it is, it returns a String for example: "(89) is prime: (true)" or "(10) is prime: false".
The result goes back over the loadbalancer to the Rest API and finally to the client.
That's how it should works.
But for most requests I get Bad Request 400. Sometimes it works fine.
400 Bad Request
Therefore I tried to do the calculation directly instead of opening a socket and it works perfectly.
So the problem is the Socket right?
Here is my Code:
package javaRestApi;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class MyRestController extends SpringBootServletInitializer {
private static Logger log = LoggerFactory.getLogger(MyRestController.class);
public static void main(String[] args) {
SpringApplication.run(MyRestController.class, args);
}
#Autowired
private AsyncService service;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MyRestController.class);
}
#PostMapping(value = "/api/request/json", consumes = MediaType.APPLICATION_JSON_VALUE)
public String jsonRequest(#RequestBody RequestModel body) throws IOException, Exception {
log.info("new Request");
CompletableFuture<String> result = service.isPrime(body.getZahl());
log.info("Result: " + result.get());
return result.get();
}
}
package javaRestApi;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class RequestModel {
private String zahl;
public void setZahl(String zahl) {
this.zahl=zahl;
}
public String getZahl() {
return zahl;
}
}
package javaRestApi;
import java.util.concurrent.Executor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsyncThread-");
executor.initialize();
return executor;
}
}
And finally the class where I create the Socket...
package javaRestApi;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.concurrent.CompletableFuture;
import javax.net.SocketFactory;
import java.net.Socket;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
#Service
public class AsyncService {
#Async("asyncExecutor")
public CompletableFuture<String> isPrime(String zahl) throws IOException, Exception {
//direct calcualtion
int z = Integer.parseInt(zahl);
if(prime(z))
return CompletableFuture.completedFuture("" + z + " is prime");
else
return CompletableFuture.completedFuture("" + z + " is no prime");
//with Tcp Connection
//return CompletableFuture.completedFuture("3"/*tcpClient(zahl)*/);
}
//local prime calculation
private static boolean prime(int zahl) {
for(int i = 2; i<zahl; i++) {
if(zahl % i == 0) {
return false;
}
}
return true;
}
// TCP connection to load balancer
private static String tcpClient(String zahl) throws IOException, Exception {
java.net.Socket socket = SocketFactory.getDefault().createSocket("localhost", 12345);
senden(socket, zahl);
return lesen(socket);
}
// send the value to Socket
static void senden(java.net.Socket socket, String zahl) throws IOException {
PrintWriter prwr = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
prwr.print(zahl);
prwr.flush();
}
// get the result from the socket
static String lesen(java.net.Socket socket) throws IOException {
int Buflength = 40;
BufferedReader bufR = new BufferedReader(new InputStreamReader(socket.getInputStream()));
char[] buf = new char[Buflength];
int anzZeichen = bufR.read(buf, 0, Buflength);
String retString = new String(buf, 0, anzZeichen);
return retString;
}
}
And here the Stacktrace.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.1.RELEASE)
2020-06-26 20:22:36.106 INFO 71152 --- [ main] javaRestApi.MyRestController : Starting MyRestController on DESKTOP-FVOQ69M with PID 71152 (C:\Users\Fill07\Desktop\javaRestApi\target\classes started by Fill07 in C:\Users\Fill07\Desktop\javaRestApi)
2020-06-26 20:22:36.110 INFO 71152 --- [ main] javaRestApi.MyRestController : No active profile set, falling back to default profiles: default
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils (file:/C:/Users/Fill07/.m2/repository/org/springframework/spring-core/5.2.7.RELEASE/spring-core-5.2.7.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2020-06-26 20:22:37.870 INFO 71152 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-06-26 20:22:37.884 INFO 71152 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-06-26 20:22:37.884 INFO 71152 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.36]
2020-06-26 20:22:38.213 INFO 71152 --- [ main] org.apache.jasper.servlet.TldScanner : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2020-06-26 20:22:38.225 INFO 71152 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-06-26 20:22:38.225 INFO 71152 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2041 ms
2020-06-26 20:22:38.356 INFO 71152 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2020-06-26 20:22:38.358 INFO 71152 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'asyncExecutor'
2020-06-26 20:22:38.845 INFO 71152 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-06-26 20:22:38.860 INFO 71152 --- [ main] javaRestApi.MyRestController : Started MyRestController in 3.385 seconds (JVM running for 3.941)
2020-06-26 20:22:40.830 INFO 71152 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-06-26 20:22:40.830 INFO 71152 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-06-26 20:22:40.841 INFO 71152 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 11 ms
2020-06-26 20:22:40.977 INFO 71152 --- [nio-8080-exec-2] javaRestApi.MyRestController : new Request
2020-06-26 20:22:41.013 INFO 71152 --- [nio-8080-exec-2] javaRestApi.MyRestController : Result: HTTP/1.1 400 Bad Request
Connection: cl
I don't understand what is wrong, i mean sometimes it works.
How can I fix it?
Is it maybe a problem with Tomcat?
Thanks for any advice.

Spring boot: Start an application automatically when Webshere Application Server starts?

Assume I have a SpringBoot Application deployed as a WAR to Websphere Application Server (WAS). This WAR contains a daemon, so it must start straight away when WAS starts (and only once).
However, I still need to activate the SpringBoot Servlet by doing a http request.
Now I understand that the concept of servlets is to act on http requests, I still want to get it auto started on appserver start. This makes my daemon portable from standalone jar/main to war/webapp.
I tried a ServletContextListener, but the contextInitalized also get only called at the first http request.
I do not have a web.xml (servlet 3).
Code:
#SpringBootApplication
#WebListener
public class DemoApplication extends SpringBootServletInitializer implements ServletContextListener {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.err.println("ONSTARTUP");
super.onStartup(servletContext);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
#Override
public void contextInitialized(ServletContextEvent sce) {
System.err.println("contextInitialized");
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
//
}
}
and:
#Component
public class DemoRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments arg0) throws Exception {
System.err.println("I AM RUNNING");
}
}
When I start WAS I first get this:
Launching defaultServer (WebSphere Application Server
16.0.0.2/wlp-1.0.13.cl160220160526-2258) on Java HotSpot(TM) 64-Bit Server VM, version 1.7.0_79-b15 (en_US)
[...]
[AUDIT ] CWWKT0016I: Web application available (default_host): http://localhost:9080/demo/
[AUDIT ] CWWKZ0001I: Application test started in 17,282 seconds.
To get my Spring Boot application starting, I first need to visit this link (http:/localhost:9080/demo/). Then it starts rolling, starting with the startup method as you can see in the log. But how can I get this starting without doing a http request?
[err] ONSTARTUP
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.0.RELEASE)
2016-09-02 10:45:52.670 INFO 23716 --- [dPool-thread-48] com.example.DemoApplication : Starting DemoApplication on [...]
2016-09-02 10:45:58.019 INFO 23716 --- [dPool-thread-48] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
[...]
[err] I AM RUNNING
[...]
2016-09-02 10:45:58.093 INFO 23716 --- [dPool-thread-48] com.example.DemoApplication : Started DemoApplication in 6.372 seconds (JVM running for 31.549)
[...]
[err] contextInitialized
[err] contextInitialized
You can change the loadOnStartup by customize the spring dispatch servlet, here is the sample question and you can use the code
#Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
return new BeanFactoryPostProcessor() {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bean = beanFactory.getBeanDefinition(
DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
bean.getPropertyValues().add("loadOnStartup", 1);
}
};
}
Reference:
how to configure 'dispatcherServlet' load on startup by spring boot?
Upate
Seems there is a more simple way, you can config it in application.properites
spring.mvc.servlet.load-on-startup=1

Spring Boot AMQP Receiver

I'm creating a simple Spring Boot app that I want to receive messages sent to an AMQP (Rabbit) exchange (from another application).
I am getting an error stating that the queue i'm to receive on does not exists. When I look at http://localhost:15672/#/queues i can see that it does; i delete it before starting this application.
I read on another post that if a queue does not exists, RabbitAdmin has to be declared and declareExchange() and declareQueue() used to overcome this.
Even with this in place I'm still seeing the same error. Furthermore, the error is confusing b/c it states:
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=404, reply-text=NOT_FOUND - no queue 'from-logger-exchange' in vhost '/', class-id=50, method-id=10)
Note the 'no queue' indicates my exchange name.
Here's the configuration code (i realize that Boot creates the connectionFactory & RabbitAdmin but started adding these when that wasn't working):
'import javax.annotation.PostConstruct;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class Config {
#Value("${spring.activemq.broker-url}")
String host;
#Value("${spring.activemq.user}")
String userName;
#Value("${spring.activemq.password}")
String password;
#Value("${config.exchangeName}")
String exchangeName;
#Value("${config.queueName}")
String queueName;
#PostConstruct
public void printVariables() {
System.out.println("host " + host);
System.out.println("userName " + userName);
System.out.println("password " + password);
System.out.println("exchangeName " + exchangeName);
System.out.println("queueName " + queueName);
System.out.println("queue.name " + queue().getName());
System.out.println("exchange.name " + topicExchange().getName());
System.out.println("binding " + binding().toString());
System.out.println("connectionFactory " + connectionFactory().toString());
System.out.println("rabbitAdmin " + rabbitAdmin().toString());
}
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host);
connectionFactory.setUsername(userName);
connectionFactory.setPassword(password);
return connectionFactory;
}
#Bean
public RabbitAdmin rabbitAdmin() {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory());
rabbitAdmin.declareExchange(topicExchange());
rabbitAdmin.declareQueue(queue());
return rabbitAdmin;
}
#Bean
public Queue queue() {
return new Queue(queueName);
}
// #Bean
// public FanoutExchange fanoutExchange() {
// return new FanoutExchange(exchangeName);
// }
public TopicExchange topicExchange() {
return new TopicExchange(exchangeName, false, true);
}
#Bean
public Binding binding() {
//return BindingBuilder.bind(queue()).to(fanoutExchange());
return BindingBuilder.bind(queue()).to(topicExchange()).with("my.routing.key");
}
#Bean
public Receiver receiver() {
return new Receiver();
}
}
'
Here's the Receiver:
import org.springframework.amqp.rabbit.annotation.RabbitListener;
public class Receiver {
#RabbitListener(queues="${config.exchangeName}")
public void receive(String input) {
System.out.println(" Receiver#receive input: " + input);
}
}
Here's the first bit of the console output:
. ____ _ __
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.3.2.RELEASE)
2016-02-23 10:20:47.710 INFO 18804 --- [ main] c.i.logging.receiver.Application : Starting Application on INT002520 with PID 18804 (C:\Users\matt.aspen\Documents\_logging\log-message-receiver2\target\classes started by matt.aspen in C:\Users\matt.aspen\Documents\_logging\log-message-receiver2)
2016-02-23 10:20:47.710 INFO 18804 --- [ main] c.i.logging.receiver.Application : No active profile set, falling back to default profiles: default
2016-02-23 10:20:47.710 DEBUG 18804 --- [ main] o.s.boot.SpringApplication : Loading source class com.intelligrated.logging.receiver.Application
2016-02-23 10:20:47.750 DEBUG 18804 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Loaded config file 'classpath:/application.properties'
2016-02-23 10:20:47.750 DEBUG 18804 --- [ main] o.s.b.c.c.ConfigFileApplicationListener : Skipped (empty) config file 'classpath:/application.properties' for profile default
2016-02-23 10:20:47.750 INFO 18804 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#679b62af: startup date [Tue Feb 23 10:20:47 PST 2016]; root of context hierarchy
2016-02-23 10:20:48.482 INFO 18804 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration' of type [class org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration$$EnhancerBySpringCGLIB$$68858653] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
host localhost
userName guest
password guest
exchangeName from-logger-exchange
queueName from-logger-queue
queue.name from-logger-queue
exchange.name from-logger-exchange
binding Binding [destination=from-logger-queue, exchange=from-logger-exchange, routingKey=my.routing.key]
connectionFactory CachingConnectionFactory [channelCacheSize=1, host=localhost, port=5672, active=true connectionFactory]
2016-02-23 10:20:48.642 INFO 18804 --- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: SimpleConnection#6239aba6 [delegate=amqp://guest#127.0.0.1:5672/]
rabbitAdmin org.springframework.amqp.rabbit.core.RabbitAdmin#5f354bcf
2016-02-23 10:20:48.753 DEBUG 18804 --- [ main] inMXBeanRegistrar$SpringApplicationAdmin : Application Admin MBean registered with name 'org.springframework.boot:type=Admin,name=SpringApplication'
2016-02-23 10:20:48.998 INFO 18804 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2016-02-23 10:20:49.208 INFO 18804 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase -2147482648
2016-02-23 10:20:49.208 INFO 18804 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2016-02-23 10:20:49.218 WARN 18804 --- [cTaskExecutor-1] o.s.a.r.listener.BlockingQueueConsumer : Failed to declare queue:from-logger-exchange
2016-02-23 10:20:49.228 WARN 18804 --- [cTaskExecutor-1] o.s.a.r.listener.BlockingQueueConsumer : Queue declaration failed; retries left=3
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[from-logger-exchange]
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:571) ~[spring-rabbit-1.5.3.RELEASE.jar:na]
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:470) ~[spring-rabbit-1.5.3.RELEASE.jar:na]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1171) [spring-rabbit-1.5.3.RELEASE.jar:na]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_40]
Caused by: java.io.IOException: null
at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:106) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:102) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:124) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.ChannelN.queueDeclarePassive(ChannelN.java:885) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.ChannelN.queueDeclarePassive(ChannelN.java:61) ~[amqp-client-3.5.7.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_40]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_40]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_40]
at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:764) ~[spring-rabbit-1.5.3.RELEASE.jar:na]
at com.sun.proxy.$Proxy31.queueDeclarePassive(Unknown Source) ~[na:na]
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:550) ~[spring-rabbit-1.5.3.RELEASE.jar:na]
... 3 common frames omitted
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'from-logger-exchange' in vhost '/', class-id=50, method-id=10)
at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:67) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:33) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:361) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:226) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:118) ~[amqp-client-3.5.7.jar:na]
... 12 common frames omitted
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'from-logger-exchange' in vhost '/', class-id=50, method-id=10)
at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:484) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:321) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:144) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:91) ~[amqp-client-3.5.7.jar:na]
at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:554) ~[amqp-client-3.5.7.jar:na]
... 1 common frames omitted
2016-02-23 10:20:54.226 WARN 18804 --- [cTaskExecutor-1] o.s.a.r.listener.BlockingQueueConsumer : Failed to declare queue:from-logger-exchange
2016-02-23 10:20:54.226 WARN 18804 --- [cTaskExecutor-1] o.s.a.r.listener.BlockingQueueConsumer : Queue declaration failed; retries left=2
What's even more confusing is that when i look at http://localhost:15672/#/connections i see the connection, http://localhost:15672/#/channels i see the channel, http://localhost:15672/#/exchanges i see "from-logger-exchange", and http://localhost:15672/#/queues i see "from-logger-queue"
One problem is your exchange is not a #Bean.
I get
reply-code=404, reply-text=NOT_FOUND - no exchange 'from.logging.exchange' in vhost '/', class-id=50, method-id=20)
with your config. Adding #Bean to the exchange fixes it.
EDIT
Also (as Artem points out below), you have the wrong property on the listener
#RabbitListener(queues="${config.exchangeName}")
should be
#RabbitListener(queues="${config.queueName}")

Resources