Spring Integration ftp inbound channel adapter recursive synchronization - recursion

With Spring Integration, is it possible to synchronize files (including subfolder's files) recursively using ftp inbound channel adapter?

No; but you can fetch a complete remote directory tree with recursive MGET with the outbound gateway...
#SpringBootApplication
#IntegrationComponentScan
public class So42324318Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So42324318Application.class, args);
List<File> files = context.getBean(Gateway.class).fetchFiles("foo/*");
System.out.println(files);
context.close();
}
#MessagingGateway(defaultRequestChannel = "fetchRecursive")
public interface Gateway {
public List<File> fetchFiles(String remoteDir);
}
#Bean
#ServiceActivator(inputChannel = "fetchRecursive")
public FtpOutboundGateway gateway() {
// Create a recursive MGET gateway that gets the remote directory from the payload
FtpOutboundGateway gateway = new FtpOutboundGateway(sessionFactory(), "mget", "payload");
gateway.setOptions("-R");
gateway.setLocalDirectoryExpression(new SpelExpressionParser().parseExpression("#remoteDirectory"));
return gateway;
}
#Bean
public SessionFactory<FTPFile> sessionFactory() {
return new CachingSessionFactory<>(ftpSF());
}
private SessionFactory<FTPFile> ftpSF() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("10.0.0.3");
sf.setUsername("ftptest");
sf.setPassword("ftptest");
sf.setClientMode(FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE);
return sf;
}
}
Result:
2017-02-19 09:55:09.351 INFO 61921 --- [ main] o.s.integration.ftp.session.FtpSession : File has been successfully transferred from: foo/bar.tx
2017-02-19 09:55:09.353 INFO 61921 --- [ main] o.s.integration.ftp.session.FtpSession : File has been successfully transferred from: foo/bar.txt
2017-02-19 09:55:09.356 INFO 61921 --- [ main] o.s.integration.ftp.session.FtpSession : File has been successfully transferred from: foo/bar/abc.txt
2017-02-19 09:55:09.358 INFO 61921 --- [ main] o.s.integration.ftp.session.FtpSession : File has been successfully transferred from: foo/baz.txt
2017-02-19 09:55:09.362 INFO 61921 --- [ main] o.s.integration.ftp.session.FtpSession : File has been successfully transferred from: foo/foo/bar/qux.txt
2017-02-19 09:55:09.364 INFO 61921 --- [ main] o.s.integration.ftp.session.FtpSession : File has been successfully transferred from: foo/foo/baz/fiz.txt
[foo/bar.tx, foo/bar.txt, foo/bar/abc.txt, foo/baz.txt, foo/foo/bar/qux.txt, foo/foo/baz/fiz.txt]

Related

use #ControllerAdvice and #ExceptionHandler but still also allow for the default exception mapping

When i use a #ControllerAdvice with the #ExceptionHandler annotation then none of the normal exception handling works anymore. All results in a HTML page with the title "HTTP Status 500 – Internal Server Error" and nothing is logged in the console.
I've created a simple #ControllerAdvice as below, when this didn't work as expected i started trying with the basePackages and extending from ResponseEntityExceptionHandler.
#ControllerAdvice(basePackages = "nl.xxxx.events")
public class EventExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(EventExceptionHandler.class);
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(EventNotFoundException.class)
public void handlePersonNotFound(RuntimeException ex) {
logger.error("error", ex);
}
}
In the service i throw the exception using:
public Event findById(Long id) {
return this.eventRepository.findById(id).orElseThrow(() -> new EventNotFoundException(id));
}
I've tried to compare the flow of code with and without #ControllerAdvice but there are numerous things that are different. For example without the #ControllerAdvice the value attribute of the HttpEntityMethodProcessor is set with the an object describing the exception. But with the #ControllerAdvice this attribute is always null.
I expected the code above to not interfere with the normal exception handling.
As a example i'd like to use the "could not initialize proxy - no Session" error because this is easy for me to reproduce.
Before i added the #ControllerAdvice spring gave the following json result:
{"timestamp":"2019-06-02T19:17:14.223+0000","status":500,"error":"Internal Server Error","message":"Could not write JSON: failed to lazily initialize a collection of role: (truncated)....","path":"/events/find"}
and with debug logging:
2019-06-02 21:18:29.723 DEBUG 1588 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : GET "/events/find?page=1&size=10", parameters={masked}
2019-06-02 21:18:29.724 DEBUG 1588 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.data.domain.Page<nl.xxxx.events.Event> nl.xxxx.events.EventController.findPersons(java.lang.String,java.lang.Integer,java.lang.Integer)
2019-06-02 21:18:29.728 DEBUG 1588 --- [nio-8080-exec-2] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json', given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-06-02 21:18:29.728 DEBUG 1588 --- [nio-8080-exec-2] m.m.a.RequestResponseBodyMethodProcessor : Writing [Page 1 of 19 containing nl.xxxx.events.Event instances]
2019-06-02 21:18:29.729 WARN 1588 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: nl.xxxx.events.Event.schedule, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: nl.xxxx.events.Event.schedule, could not initialize proxy - no Session (through reference chain: org.springframework.data.domain.PageImpl["content"]->java.util.Collections$UnmodifiableRandomAccessList[0]->nl.xxxx.events.Event["schedule"])]
2019-06-02 21:18:29.729 DEBUG 1588 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 500 INTERNAL_SERVER_ERROR
2019-06-02 21:18:29.729 DEBUG 1588 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error?page=1&size=10", parameters={masked}
2019-06-02 21:18:29.730 DEBUG 1588 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-06-02 21:18:29.731 DEBUG 1588 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-06-02 21:18:29.731 DEBUG 1588 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Sun Jun 02 21:18:29 CEST 2019, status=500, error=Internal Server Error, message=Could not (truncated)...]
2019-06-02 21:18:29.732 DEBUG 1588 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 500
But now it returns:
<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 500 – Internal Server Error</h1></body></html>
And with debug logging:
2019-06-02 21:20:33.682 DEBUG 22308 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/events/find?page=1&size=10", parameters={masked}
2019-06-02 21:20:33.688 DEBUG 22308 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.data.domain.Page<nl.xxxx.events.Event> nl.xxxx.events.EventController.findPersons(java.lang.String,java.lang.Integer,java.lang.Integer)
2019-06-02 21:20:33.760 INFO 22308 --- [nio-8080-exec-1] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2019-06-02 21:20:33.900 DEBUG 22308 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json', given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-06-02 21:20:33.901 DEBUG 22308 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Writing [Page 1 of 19 containing nl.xxxx.events.Event instances]
2019-06-02 21:20:33.923 DEBUG 22308 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Using #ExceptionHandler public final org.springframework.http.ResponseEntity<java.lang.Object> org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception
2019-06-02 21:20:33.925 DEBUG 22308 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : No match for [application/json, text/plain, */*], supported: []
2019-06-02 21:20:33.926 DEBUG 22308 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: nl.xxxx.events.Event.schedule, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: nl.xxxx.events.Event.schedule, could not initialize proxy - no Session (through reference chain: org.springframework.data.domain.PageImpl["content"]->java.util.Collections$UnmodifiableRandomAccessList[0]->nl.xxxx.events.Event["schedule"])]
2019-06-02 21:20:33.926 DEBUG 22308 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 500 INTERNAL_SERVER_ERROR
So, at some point it just says No match for [application/json, text/plain, */*], supported: [] and wont return a nice JSON exception anymore.
Any ideas at what i'm doing wrong? initially is just want EventNotFoundException to result in a 404 instead of a 500. Propably in the future i'd like to add content aswell.
The problem is that you try to handle RuntimeException which is too general. public void handlePersonNotFound(RuntimeException ex) You also don't need to extend from ResponseEntityExceptionHandler.
If you write an ExceptionHandler that should handle a specific exception, the handleException method needs to accept that specific exception as parameter. Also you would probably want some sort of repsonse.
#ControllerAdvice
public class EventExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(getClass());
#ResponseBody
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(EventNotFoundException.class)
public String handlePersonNotFound(EventNotFoundException ex) {
logger.error("error", ex);
return ex.getMessage();
}
}
This will give a basic reponse body with the exception message and error code.

Spring Cloud stream binder Kafka custom headers are not received in consumer

I'm using Spring cloud stream binder kafka, Edgware.SR4 release.
I have set custom headers to a message payload and published it but i can't see those headers in consumer end.
I have used Message object to bind payload and headers. I have tried adding the property spring.cloud.stream.kafka.binder.headers but it did not work
Producer:
Application.yml
spring:
cloud:
stream:
bindings:
sampleEvent:
destination: sample-event
content-type: application/json
kafka:
binder:
brokers: localhost:9092
zkNodes: localhost:2181
autoCreateTopics: false
zkConnectionTimeout: 36000
MessageChannelConstants.java
public class MessageChannelConstants {
public static final String SAMPLE_EVENT = "sampleEvent";
private MessageChannelConstants() {}
}
SampleMessageChannels.java
public interface SampleMessageChannels {
#Output(MessageChannelConstants.SAMPLE_EVENT)
MessageChannel sampleEvent();
}
SampleEventPublisher.java
#Service
#EnableBinding(SampleMessageChannels.class)
public class SampleEventPublisher{
#Autowired
private SampleMessageChannels sampleMessageChannels;
public void publishSampleEvent(SampleEvent sampleEvent) {
final Message<SampleEvent> message = MessageBuilder.withPayload(sampleEvent).setHeader("appId", "Demo").build();
MessageChannel messageChannel = SampleMessageChannels.sampleEvent();
if (messageChannel != null) {
messageChannel.send(message);
}
}
}
Consumer:
application.yml
spring:
cloud:
stream:
bindings:
sampleEvent:
destination: sample-event
content-type: application/json
kafka:
binder:
brokers: localhost:9092
zkNodes: localhost:2181
autoCreateTopics: false
zkConnectionTimeout: 36000
MessageChannelConstants.java
public class MessageChannelConstants {
public static final String SAMPLE_EVENT = "sampleEvent";
private MessageChannelConstants() {}
}
SampleMessageChannels.java
public interface SampleMessageChannels {
#Output(MessageChannelConstants.SAMPLE_EVENT)
MessageChannel sampleEvent();
}
SampleEventListener.java
#Service
#EnableBinding(SampleMessageChannels.class)
public class SampleEventListener{
#StreamListener(MessageChannelConstants.SAMPLE_EVENT)
public void listenSampleEvent(#Payload SampleEvent event,
#Header(required = true, name = "appId") String appId) {
// do something
}
Below is the Exception I got,
org.springframework.messaging.MessageHandlingException: Missing header 'appId' for method parameter type [class java.lang.String]
at org.springframework.messaging.handler.annotation.support.HeaderMethodArgumentResolver.handleMissingValue(HeaderMethodArgumentResolver.java:100)
at org.springframework.messaging.handler.annotation.support.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:103)
at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:112)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:135)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:107)
at org.springframework.cloud.stream.binding.StreamListenerMessageHandler.handleRequestMessage(StreamListenerMessageHandler.java:55)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:360)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:271)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:188)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.channel.FixedSubscriberChannel.send(FixedSubscriberChannel.java:70)
at org.springframework.integration.channel.FixedSubscriberChannel.send(FixedSubscriberChannel.java:64)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:188)
at org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter.access$200(KafkaMessageDrivenChannelAdapter.java:63)
at org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter$IntegrationRecordMessageListener.onMessage(KafkaMessageDrivenChannelAdapter.java:372)
at org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter$IntegrationRecordMessageListener.onMessage(KafkaMessageDrivenChannelAdapter.java:352)
at org.springframework.kafka.listener.adapter.RetryingAcknowledgingMessageListenerAdapter$1.doWithRetry(RetryingAcknowledgingMessageListenerAdapter.java:79)
at org.springframework.kafka.listener.adapter.RetryingAcknowledgingMessageListenerAdapter$1.doWithRetry(RetryingAcknowledgingMessageListenerAdapter.java:73)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:180)
at org.springframework.kafka.listener.adapter.RetryingAcknowledgingMessageListenerAdapter.onMessage(RetryingAcknowledgingMessageListenerAdapter.java:73)
at org.springframework.kafka.listener.adapter.RetryingAcknowledgingMessageListenerAdapter.onMessage(RetryingAcknowledgingMessageListenerAdapter.java:39)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:792)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:736)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.access$2100(KafkaMessageListenerContainer.java:246)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer$ListenerInvoker.run(KafkaMessageListenerContainer.java:1025)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)
Note: I am using spring cloud sleuth and zipkin dependency as well.
With Edgware (SCSt Ditmars), you have to specify which headers will be transferred.
See Kafka Binder Properties.
This is because Edgware was based on Kafka before it supported headers natively and we encode the headers into the payload.
spring.cloud.stream.kafka.binder.headers
The list of custom headers that will be transported by the binder.
Default: empty.
You should also be sure to upgrade spring-kafka to 1.3.9.RELEASE and kafka-clients to 0.11.0.2.
Preferably, though, upgrade to Finchley or Greemwich. Those versions support headers natively.

Spring Rest Controller method not invoked with path variable and http request as arguments

This is my controller method
#RestController
public class ProfileController {
#GetMapping("/quiz/{quizId}/identifyfromsixjson")
#ResponseBody
UserProfileQuestion playIdentifyFromSix(#PathVariable String quizId, HttpServletRequest request) {
... Calling service method ... here
}
}
application.properties
server.contextPath=/myproject
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
So when I make a GET request to http://localhost:8080/myproject/identifyfromsixjson/test, this is the response I see in Postman.
{
"timestamp": "2018-10-08T02:42:14.387+0000",
"status": 405,
"error": "Method Not Allowed",
"message": "Request method 'GET' not supported",
"path": "/myproject/quiz/test/identifyfromsixjson"
}
Startup logs
018-10-08 01:59:32.603 WARN 46035 --- [ main] aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2018-10-08 01:59:32.641 INFO 46035 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/quiz/{quizId}/identifyfromsixjson]}" onto public org.springframework.http.ResponseEntity<com.myproject.model.UserProfileQuestion> com.myproject.controller.ProfileController.fetchUserProfileAndHeadShot(java.lang.String,javax.servlet.http.HttpServletRequest)
2018-10-08 01:59:32.644 INFO 46035 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-10-08 01:59:32.644 INFO 46035 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-10-08 01:59:32.672 INFO 46035 --- [ main] o.s.w.s.h.BeanNameUrlHandlerMapping : Mapped URL path [/myproject] onto handler '/myproject'
2018-10-08 01:59:32.678 INFO 46035 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-08 01:59:32.678 INFO 46035 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
What am I doing wrong?
This is the path you've defined:
/quiz/{quizId}/identifyfromsixjson
and this is the path you're testing with
/identifyfromsixjson/test
It should be apparent that they don't match, which is why you're getting that error.
You can do the following:
1. Test with the path that you've defined:
http://localhost:8080/myproject/quiz/test/identifyfromsixjson
2. Update your path definition
#GetMapping("/identifyfromsixjson/{quizId}")
#ResponseBody
UserProfileQuestion playIdentifyFromSix(#PathVariable String quizId,HttpServletRequest request) {
... Calling service method ... here
}
and then test with
http://localhost:8080/myproject/identifyfromsixjson/test
Looks like you want to write a RestController. Annotate your controller with #RestController
#RestController
public class QuizController {
#GetMapping("/identifyfromsixjson/{quizId}")
#ResponseBody
UserProfileQuestion playIdentifyFromSix(#PathVariable String quizId, HttpServletRequest request) {
... Calling service method ... here
}
}

Log4J not working properly with spring boot mvc api

I am new to SpringBoot and I am trying to configure the Log4J but i am facing a wired behavior, please help.
This is my project structure:
enter image description here
Below is code of my files:-
Log4J2PropertiesConf:
package Log4J;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Log4J2PropertiesConf {
private static Logger logger = LogManager.getLogger();
public static void main(String[] args) {
logger.info("Started");
TestClass.test("Before");
SpringApplication.run(Log4J2PropertiesConf.class, args);
TestClass.test("After");
}
}
TestClass:
package Log4J;
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
public class TestClass {
private static Logger logger = LogManager.getLogger(TestClass.class);
public static void test(String s) {
logger.info("IN Class Test: " + s);
}
}
Testcontroller:
package Log4J.controller;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class Testcontroller {
private static Logger logger = LogManager.getLogger(Testcontroller.class);
#RequestMapping(method = RequestMethod.GET, value = "/Get")
public ResponseEntity<String> GetValue() {
logger.debug("This is a debug message: Test1");
logger.info("This is an info message");
logger.warn("This is a warn message");
logger.error("This is an error message");
logger.fatal("This is a fatal message");
return new ResponseEntity<String>("Reached", HttpStatus.OK);
}
}
Log4J2PropertiesConfTest:
package Log4J;
import org.junit.Test;
public class Log4J2PropertiesConfTest {
#Test
public void testPerformSomeTask() throws Exception {
TestClass.test("from Test");
}
}
log4j2.properties:
name=PropertiesConfig
property.filename = logs
appenders = console, file
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
appender.file.type = File
appender.file.name = LOGFILE
appender.file.fileName=${filename}/propertieslogs.log
appender.file.layout.type=PatternLayout
appender.file.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
loggers=file
logger.file.name=Log4J
logger.file.level = debug
logger.file.appenderRefs = file
logger.file.appenderRef.file.ref = LOGFILE
logger.file.additivity = false
rootLogger.level = debug
rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = STDOUT
That is all i am trying, I am using maven and I have all the dependencies added in pom file.
Now when I am running this as Springboot app, I am getting below on console:
[main] INFO org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer - Tomcat started on port(s): 10053 (http)
[main] INFO Log4J.Log4J2PropertiesConf - Started Log4J2PropertiesConf in 2.982 seconds (JVM running for 3.868)
2017-01-04 21:59:13.832 INFO 7544 --- [ main] L.TestClass : **IN Class Test: After**
AND I am getting below in log file at the same time:
[INFO ] 2017-01-04 21:59:10.740 [main] Log4J2PropertiesConf - **Started**
[INFO ] 2017-01-04 21:59:10.742 [main] TestClass - **IN Class Test: Before**
Now if i invoke the GetValue(http:localhost:10053/Get) method, I am getting below on console:
2017-01-04 22:02:18.531 INFO 7544 --- [io-10053-exec-1] L.c.Testcontroller : **This is an info message**
2017-01-04 22:02:18.532 WARN 7544 --- [io-10053-exec-1] L.c.Testcontroller : **This is a warn message**
2017-01-04 22:02:18.532 ERROR 7544 --- [io-10053-exec-1] L.c.Testcontroller : **This is an error message**
2017-01-04 22:02:18.532 FATAL 7544 --- [io-10053-exec-1] L.c.Testcontroller : **This is a fatal message**
AND nothing gets updated in log.
When I run the unit test, i get below in log file:
[INFO ] 2017-01-04 22:03:36.650 [main] TestClass - **IN Class Test: from Test**
So, I am expecting every log entry to go into the log file, not to console. Not sure how it is putting some information to console and some to file, although I have logger.file.additivity = false in my properties file also.
I want to have everything to go into the log file. Please help.

Spring ServletRegistrationBean not loaded in JUnit Test

I'm trying to test a Rest Service in my Spring Boot Application.
I can do ir successfully when I run or debug the whole application. Spring Bott start shows the following lines, among others:
2015-07-02 17:04:10.654 INFO 3084 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.0.20
2015-07-02 17:04:10.769 INFO 3084 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2015-07-02 17:04:10.769 INFO 3084 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2449 ms
2015-07-02 17:04:11.219 INFO 3084 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2015-07-02 17:04:11.224 INFO 3084 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2015-07-02 17:04:11.224 INFO 3084 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
However, when I run my JUnit test, everything works fine, request mappings work, etc, except that the servlet is not initialized. Indeed, those lines above do not appear.
That's strange, because I'm running the test using the same Application.class which I use to run the whole application. This is Application.class:
#ImportResource("classpath:/META-INF/spring/integration/spring-integration-context.xml")
#SpringBootApplication
#WebAppConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And from my JUnit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
public class CatalogRestControllerTests extends AbstractTransactionalJUnit4SpringContextTests {
...
#Test
public void someTest() {...}
}
I've been trying to fix the problem from servlet-context.xml and web.xml configuration files. But I don't think the problem is there, since it works for Application.class
Any idea?
Thank you!
Found it.
So it seems I was missing #WebIntegrationTest annotation on my JUnit class. This is what documentation says about #WebIntegrationTest:
Test class annotation signifying that the tests are "web integration
tests" and therefore require full startup in the same way as a
production application (listening on normal ports). Normally used in
conjunction with #SpringApplicationConfiguration,
This annotation can be used as an alternative to #IntegrationTest and
#WebAppConfiguration.

Resources