I've watched nice video https://www.youtube.com/watch?v=Kohfw-0xc6o&t=321s how to work with rest and get data from webserver, the problem is even thou I did everything nice and understand what and why is done, I cannot connect with server. My code is:
Main app:
#SpringBootApplication
class Lab3Application {
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
static void main(String[] args) {
SpringApplication.run(Lab3Application, args)
}
}
Controller:
#RestController
public class MyRestController {
#Autowired
RestTemplate restTemplate;
#Autowired
MyCallable myCallable;
#RequestMapping(value = "/myapp")
public String myApp() {
System.out.println("WOWOWOW");
long start = System.currentTimeMillis();
for(int i = 0; i < 10; i++) {
System.out.println("WOWOWOW");
String response = restTemplate.getForObject("https://c3f837c7-3088-4165-a4f3-ad11b24c41f1.mock.pstmn.io", String.class);
System.out.println(response);
}
long finish = System.currentTimeMillis();
System.out.println("TIME: " + (finish - start) + " ms");
return "success";
}
#RequestMapping(value = "/newApp")
public String newApp() {
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Future<String>> list = new ArrayList<>();
long start = System.currentTimeMillis();
for(int i = 0; i < 200; i++) {
Future<String> future = executorService.submit(myCallable);
list.add(future);
}
for(Future<String> f : list) {
try { f.get(); }
catch(Exception ex) { ex.printStackTrace(); }
}
executorService.shutdown();
long finish = System.currentTimeMillis();
System.out.println("TIME: " + (finish - start) + " ms");
return "success";
}
}
For future:
#Component
public class MyCallable implements Callable<String> {
#Autowired
RestTemplate restTemplate;
#Override
public String call() throws Exception {
return restTemplate.getForObject("https://c3f837c7-3088-4165-a4f3-ad11b24c41f1.mock.pstmn.io", String.class);
}
}
And now in intelliJ I click 'Run' and I've got:
:: Spring Boot :: (v2.2.2.RELEASE)
2019-12-09 10:53:18.614 INFO 11920 --- [ main] com.example.lab3.Lab3Application : Starting Lab3Application on Acer-Aspire with PID 11920 (C:\Users\kamsz\Downloads\lab3\target\classes started by kamsz in C:\Users\kamsz\Downloads\lab3)
2019-12-09 10:53:18.617 INFO 11920 --- [ main] com.example.lab3.Lab3Application No active profile set, falling back to default profiles: default
2019-12-09 10:53:19.218 INFO 11920 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.ws.config.annotation.DelegatingWsConfiguration' of type [org.springframework.ws.config.annotation.DelegatingWsConfiguration$$EnhancerBySpringCGLIB$$81724b8b] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-09 10:53:19.249 INFO 11920 --- [ main] .w.s.a.s.AnnotationActionEndpointMapping : Supporting [WS-Addressing August 2004, WS-Addressing 1.0]
2019-12-09 10:53:19.450 INFO 11920 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-12-09 10:53:19.460 INFO 11920 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-12-09 10:53:19.464 INFO 11920 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.29]
2019-12-09 10:53:19.559 INFO 11920 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-12-09 10:53:19.559 INFO 11920 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 879 ms
2019-12-09 10:53:19.719 INFO 11920 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-12-09 10:53:20.187 INFO 11920 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-12-09 10:53:20.187 INFO 11920 --- [ main] com.example.lab3.Lab3Application : Started Lab3Application in 1.926 seconds (JVM running for 3.233)
I've also created mock server using Postnam, so if in browser I go to: https://c3f837c7-3088-4165-a4f3-ad11b24c41f1.mock.pstmn.io/myapp I've got string {"status": "ok"} written.
My question is how to run this app to execude code in for loops (restTemplate.getObject...) becouse right now I've got just Started Application and no strings. I've iven added
System.out.println("WOWOWOW");
in random places to check if it goes there and it doesn't. I'm new to that so please, help me run it properly so I could play with it little more and learn how to do it.
Related
I'm overriding the onFileCreate() method of the org.apache.commons.io.monitor.FileAlterationListener interface.
The method works, but I found that sometimes it spawns two threads and I don't fully understand what is triggering this behaviour.
WatchService Class
#Component
#Slf4j(topic="watchService")
public class WatchService {
private static RestTemplate restTemplate;
private static Environment env;
#Autowired
public WatchService(RestTemplate restTemplate, Environment env) {
WatchService.restTemplate = restTemplate;
WatchService.env = env;
}
//When a new file is created inside a folder, the file content is sent to kafka
#Bean
public static void startFolderPolling() throws Exception {
FileAlterationObserver observer = new FileAlterationObserver(env.getRequiredProperty("folder"));
FileAlterationMonitor monitor = new FileAlterationMonitor(5000);
log.info("setup completed");
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
#Override
public void onFileCreate(File file) {
log.info("are you single thread ?");
try {
String data = FileUtils.readFileToString(file, "UTF-8");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(data,headers);
log.info("Calling Kakfa microservice");
String answer = restTemplate.postForObject("http://kafka/api/messages/receiveSapOrder", entity, String.class);
log.info("sending SAP Order result:" + answer);
} catch (IOException e) {
e.printStackTrace();
}
}
};
observer.addListener(listener);
monitor.addObserver(observer);
monitor.start();
}
}
Main Method
#SpringBootApplication
#EnableEurekaClient
public class DirectoryListenerApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(DirectoryListenerApplication.class, args);
startFolderPolling();
}
}
With the same file created in the folder sometimes the method logs two calls in two separated threads, sometimes the method log only one call in a single thread.
2022-05-10 09:46:42.382 INFO 88330 --- [ main] watchService : setup completed
2022-05-10 09:46:42.397 INFO 88330 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SAP-LISTENER/192.168.2.63:sap-listener:8095 - registration status: 204
2022-05-10 09:46:57.394 INFO 88330 --- [ Thread-4] watchService : are you single thread ?
2022-05-10 09:46:57.423 INFO 88330 --- [ Thread-4] watchService : Calling Kakfa microservice
2022-05-10 09:46:58.788 INFO 88330 --- [ Thread-4] watchService : sending SAP Order result:{"message":"Uploaded the file successfully"}
2022-05-10 09:47:00.108 INFO 88330 --- [ Thread-2] watchService : are you single thread ?
2022-05-10 09:47:00.112 INFO 88330 --- [ Thread-2] watchService : Calling Kakfa microservice
2022-05-10 09:47:00.197 INFO 88330 --- [ Thread-2] watchService : sending SAP Order result:{"message":"Uploaded the file successfully"}
Is it possible to force the single thread behaviour ?
I removed the SprigBoot #Bean notation over my startFolderPolling method and now only one thread is created.
I have defined a function in my Spring Cloud Function project, but after executing function (ScanQrCode) as AS Lambda I get:
2020-07-13 10:19:04.592 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Started LambdaRTEntry in 26.357 seconds (JVM running for 27.777)
2020-07-13 10:19:04.653 INFO 1 --- [ main] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function 'function' with acceptedOutputTypes: []
2020-07-13 10:19:04.731 INFO 1 --- [ main] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function 'consumer' with acceptedOutputTypes: []
2020-07-13 10:19:04.734 INFO 1 --- [ main] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function 'supplier' with acceptedOutputTypes: []
2020-07-13 10:19:04.754 INFO 1 --- [ main] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function '' with acceptedOutputTypes: []
2020-07-13 10:19:04.811 INFO 1 --- [ main] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function '' with acceptedOutputTypes: []
2020-07-13 10:19:04.811 INFO 1 --- [ main] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function '' with acceptedOutputTypes: []
No function defined: java.lang.IllegalStateException
java.lang.IllegalStateException: No function defined
at org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer.apply(AbstractSpringFunctionAdapterInitializer.java:187)
at org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler.handleRequest(SpringBootRequestHandler.java:51)
The weired thing here is that I have commented out the consume, supplier and function functions...I don´t know why Spring or AWS Lambda considered them...
Here are my classes:
Spring Boot App
#SpringBootApplication
public class FoodhatQrApplication {
public static void main(String[] args) {
SpringApplication.run(FoodhatQrApplication.class, args);
}
// #Bean
// public Function<String, String> function(){
// return input -> input;
// }
//
// #Bean
// public Consumer<String> consume(){
// return input -> System.out.println("Input: " + input);
// }
//
// #Bean
// public Supplier<String> supply(){
// return () -> "Hello World";
// }
}
Function class
#Component
public class ScanQrCode implements Function<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent>{
#Autowired
private QrCodeRepository qrCodeRepository;
#Override
public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent request) {
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
response.setStatusCode(302);
response.setHeaders(Collections.singletonMap("location" , "http://www.google.de"));
return response;
}
}
What am I missing?
Function Component Scan Adding spring.cloud.function.scan.packages might help.
Reference: https://cloud.spring.io/spring-cloud-function/reference/html/spring-cloud-function.html#_function_component_scan
You definitely need to add your function name in our case it "ScanQrCode" to AWS lambda env variables.
FUNCTION_NAME: ScanQrCode
While saving data into MySQL database i am getting below SQL error.
SQL Warning Code: -1100 SQLState 02000 no data error with Spring Boot JPA Data is not updated into DB, Please find Below repository method and Bean Entity. SpringBoot version 2.2.5, Java Version 11, MySQL 5.5 version.
#Modifying(clearAutomatically = true)
#Transactional
public List<Status> resetStatus()
{
logger.info("in resetStatus: reset the value null");
String setBoxval = null;
String setBoxId = null;
List<Status> retRes = new ArrayList<Status>();
Status s = new Status();
try {
entityManager.createQuery("UPDATE UserEntity ue SET ue.boxvalue = :boxVal, boxid = :boxId")
.setParameter("boxVal", setBoxval)
.setParameter("boxId", setBoxId)
.executeUpdate();
s.setResponse("Success");
} catch(Exception ex) {
s.setResponse("failue");
ex.printStackTrace();
}
retRes.add(s);
return retRes;
}
Entity bean:
#Entity
#Table(name="tictacbox")
public class UserEntity
{
#Id
#GeneratedValue
private int id;
#Column(name="boxid")
private Integer boxid;
#Column(name="boxvalue")
private String boxvalue;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Integer getBoxid() {
return boxid;
}
public void setBoxid(Integer boxid) {
this.boxid = boxid;
}
public String getBoxvalue() {
return boxvalue;
}
public void setBoxvalue(String boxvalue) {
this.boxvalue = boxvalue;
}
#Override
public String toString() {
return "userEntity [id=" + id + ", boxId=" + boxid +
", boxvalue=" + boxvalue + "]";
}
}
Log:
2020-04-03 11:25:45.937 INFO 18796 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-03 11:25:45.943 INFO 18796 --- [ main] com.test.TicTacToe.TicTacToeApplication : Started TicTacToeApplication in 12.471 seconds (JVM running for 13.484)
2020-04-03 11:26:13.855 INFO 18796 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-04-03 11:26:13.856 INFO 18796 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-04-03 11:26:13.872 INFO 18796 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 16 ms
2020-04-03 11:26:30.427 INFO 18796 --- [nio-8080-exec-8] com.test.TicTacToe.user.HomeController : resetStatus
2020-04-03 11:26:30.427 INFO 18796 --- [nio-8080-exec-8] com.test.TicTacToe.user.UserService : in service: resetStatus
2020-04-03 11:26:30.457 INFO 18796 --- [nio-8080-exec-8] com.test.TicTacToe.user.UserService : in resetStatus: reset the value null
2020-04-03 11:26:30.839 WARN 18796 --- [nio-8080-exec-8] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: -1100, SQLState: 02000
2020-04-03 11:26:30.841 WARN 18796 --- [nio-8080-exec-8] o.h.engine.jdbc.spi.SqlExceptionHelper : no data
I'm using Spring Webflux and as I understand it, by using this, the thread used for receiving request and the one used for response should be different. However, whether I use netty or undertow, I end up using the same thread.
My app is a simple crud app with MySQL DB. I'm not using r2dbc but a jdbc coupled with Executor and Scheduler.
As shown in the log below, request is handled by thread [ XNIO-1 I/O-6] and the response is given by the same one.
By this, I'm assuming the thread is blocked until db operation has finished. How can I fix this?
Here's the log
2019-07-23 17:49:10.051 INFO 132 --- [ main] org.xnio : XNIO version 3.3.8.Final
2019-07-23 17:49:10.059 INFO 132 --- [ main] org.xnio.nio : XNIO NIO Implementation Version 3.3.8.Final
2019-07-23 17:49:10.114 INFO 132 --- [ main] o.s.b.w.e.undertow.UndertowWebServer : Undertow started on port(s) 8080 (http)
2019-07-23 17:49:10.116 INFO 132 --- [ main] c.n.webflux.demo.WebfluxFunctionalApp : Started WebfluxFunctionalApp in 1.262 seconds (JVM running for 2.668)
2019-07-23 17:49:10.302 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [4c85975] HTTP GET "/api/findall"
2019-07-23 17:49:10.322 DEBUG 132 --- [ XNIO-1 I/O-6] s.w.r.r.m.a.RequestMappingHandlerMapping : [4c85975] Mapped to public reactor.core.publisher.Mono<java.util.List<com.webflux.demo.model.TypeStatus>> com.webflux.demo.controller.MonitoringController.findAll()
2019-07-23 17:49:10.337 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler : Using 'application/json;charset=UTF-8' given [*/*] and supported [application/json;charset=UTF-8, application/*+json;charset=UTF-8, text/event-stream]
2019-07-23 17:49:10.338 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler : [4c85975] 0..1 [java.util.List<com.webflux.demo.model.TypeStatus>]
2019-07-23 17:49:10.347 INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-23 17:49:10.785 INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-23 17:49:10.838 DEBUG 132 --- [pool-1-thread-1] org.springframework.web.HttpLogging : [4c85975] Encoding [[com.webflux.demo.model.TypeStatus#7b4509cb, com.webflux.demo.model.TypeStatus#22676ebe, (truncated)...]
2019-07-23 17:49:10.949 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [4c85975] Completed 200 OK
Also my dao is
#Repository
public class TypeStatusJdbcTemplate {
private JdbcTemplate jdbcTemplate;
public TypeStatusJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private final static String SQL_FIND_ALL = "select * from `monitoring`.`type_status` limit 3";
public List<TypeStatus> findAll() {
return jdbcTemplate.query(SQL_FIND_ALL,
new TypeStatusMapper());
}
}
service is
#Service
public class MonitoringService {
private final Scheduler scheduler;
private TypeStatusJdbcTemplate repository;
public MonitoringService(Scheduler scheduler, TypeStatusJdbcTemplate repository) {
this.scheduler = scheduler;
this.repository = repository;
}
public Mono<List<TypeStatus>> findAll() {
return Mono.fromCallable(repository::findAll).subscribeOn(scheduler);
}
}
controller is
#RestController
#RequestMapping("/api")
public class MonitoringController {
private final MonitoringService monitoringService;
private static final Logger logger = LoggerFactory.getLogger(MonitoringController.class);
public MonitoringController(MonitoringService monitoringService) {
this.monitoringService = monitoringService;
}
#GetMapping(value="/findall")
public Mono<List<TypeStatus>> findAll() {
return monitoringService.findAll();
}
}
main file (showing scheduler)
#SpringBootApplication
public class WebfluxFunctionalApp {
public static void main(String[] args){
SpringApplication.run(WebfluxFunctionalApp.class, args);
}
#PostConstruct
public void init(){
// Setting Spring Boot SetTimeZone
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
#Bean
public Scheduler jdbcScheduler() {
return Schedulers.fromExecutor(Executors.newFixedThreadPool(30));
}
}
Thread execution does not always have to be different threads. Taken from the Reactive documentation:
Reactive Schedulers
Obtaining a Flux or a Mono doesn’t necessarily mean it will run in a dedicated Thread. Instead, most operators continue working in the Thread on which the previous operator executed. Unless specified, the topmost operator (the source) itself runs on the Thread in which the subscribe() call was made.
So there is nothing that says that it has to be a new thread.
I have a web application that should only be callable from specific IP addresses. Other than that, there is no need for authentication or for authorization; if you're coming from the right IP, you can see everything.
To that end, searching StackOverflow and other places, I found a number of suggestions for filtering requests by IP address in Spring Security. They all took this form (extending WebSecurityConfigurerAdapter using java configuration):
http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')");
However, that never worked for me; it never rejected any request, no matter what IP address I made the request from. Instead, I implemented my IP filtering with a custom filter like this:
#Configuration
#EnableWebSecurity
#PropertySource("classpath:config.properties")
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
private Environment env;
#Override
protected void configure(HttpSecurity http) throws Exception {
String ipRange = env.getRequiredProperty("trusted_ips");
logger.info("##### SETTING UP SECURITY CONFIGURATION #############");
logger.info("## trusted_ips: " + ipRange);
logger.info("##### SETTING UP SECURITY CONFIGURATION - END #############");
http.addFilterBefore(new IPSecurityFilter(ipRange), J2eePreAuthenticatedProcessingFilter.class)
.authorizeRequests().antMatchers("/**").permitAll();
}
}
My IPSecurityFilter:
public class IPSecurityFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(IPSecurityFilter.class);
private String[] ipAddresses;
public IPSecurityFilter(String strIPAddresses) {
logger.info("#### Our IP Address whitelist: " + strIPAddresses);
this.ipAddresses = strIPAddresses.split(",");
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
logger.info("Checking whether request should be allowed: " + request.getRequestURI());
logger.info("### Request is coming from IP address: " + request.getRemoteAddr());
for (String ipAddress : ipAddresses) {
if (ipAddress.equals(request.getRemoteAddr())) {
logger.info("### Allowing request from ip address: " + request.getRemoteAddr());
return; // We accept requests from this IP address
}
}
// The remote IP address isn't on our white list; throw an exception
throw new AccessDeniedException("Access has been denied for your IP address: " + request.getRemoteAddr());
}
}
This seems to work in that the request is rejected if it originates from an IP Address that isn't on my white list.
However, with this configuration, my unit (using MockMvc) test fails; and it fails in a way that I would never have expected. When the unit test runs, it appears to use the Spring Security configuration correctly and the request passes the security test (the IP white list includes 127.0.0.1 and according to the log that is generated while the test is being run, the request is coming from that IP). However, the request never seems to be routed to my controller.
Here is my test:
#RunWith(SpringRunner.class)
#WebMvcTest()
//#WebMvcTest(value = HandlerController.class)
#AutoConfigureMockMvc
#Import(SecurityConfig.class)
public class HandlerControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void getIndex() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
}
}
And finally, here is my controller (please ignore the idiotic way that I'm generating the JSON return value, it's still very early in development):
#RestController
public class HandlerController {
private static final Logger logger = LoggerFactory.getLogger(HandlerController.class);
#RequestMapping("/")
public String index() {
logger.info("### handling a request for / ###");
return "{\"services\":[\"OutboundMessageService\"]}";
}
}
And here are the test results:
2017-11-14 08:29:12.151 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : Starting HandlerControllerTest on 597NLL1 with PID 25412 (started by User in C:\Development\KnowledgeBin\NewArchitecture\OutboundMessageHandler)
2017-11-14 08:29:12.152 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : No active profile set, falling back to default profiles: default
2017-11-14 08:29:12.178 INFO 25412 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Refreshing org.springframework.web.context.support.GenericWebApplicationContext#209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
2017-11-14 08:29:13.883 INFO 25412 --- [ main] b.a.s.AuthenticationManagerConfiguration :
Using default security password: 56e3fab8-f7fb-4fbd-b2d2-e37eae8cef5e
2017-11-14 08:29:13.962 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : #### Our IP Address whitelist: 122.22.22.22,127.0.0.1
2017-11-14 08:29:14.086 INFO 25412 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#3f4f9acd, org.springframework.security.web.context.SecurityContextPersistenceFilter#470a9030, org.springframework.security.web.header.HeaderWriterFilter#60c16548, org.springframework.security.web.csrf.CsrfFilter#435ce306, org.springframework.security.web.authentication.logout.LogoutFilter#607b2792, com.zpaper.services.security.IPSecurityFilter#46baf579, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#27494e46, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#36453307, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#4bf324f9, org.springframework.security.web.session.SessionManagementFilter#452c8a40, org.springframework.security.web.access.ExceptionTranslationFilter#39ce27f2, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#5767b2af]
2017-11-14 08:29:14.183 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.index()
2017-11-14 08:29:14.184 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/OutboundMessageService]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.outboundMessage()
2017-11-14 08:29:14.189 INFO 25412 --- [ 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.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-11-14 08:29:14.190 INFO 25412 --- [ 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.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-11-14 08:29:14.243 INFO 25412 --- [ main] c.z.s.config.HandlerWebConfiguration : #### My Configuration handler was called ####
2017-11-14 08:29:14.253 INFO 25412 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler]
2017-11-14 08:29:14.313 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for #ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext#209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
2017-11-14 08:29:14.784 INFO 25412 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
2017-11-14 08:29:14.784 INFO 25412 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization started
2017-11-14 08:29:14.805 INFO 25412 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization completed in 21 ms
2017-11-14 08:29:14.897 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : Started HandlerControllerTest in 3.095 seconds (JVM running for 3.995)
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : Checking whether request should be allowed: /
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : ### Request is coming from IP address: 127.0.0.1
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : ### Allowing request from ip address: 127.0.0.1
MockHttpServletRequest:
HTTP Method = GET
Request URI = /
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.363 sec <<< FAILURE! - in com.zpaper.services.controllers.HandlerControllerTest
getIndex(com.zpaper.services.controllers.HandlerControllerTest) Time elapsed: 0.12 sec <<< ERROR!
org.json.JSONException: Unparsable JSON string:
at org.skyscreamer.jsonassert.JSONParser.parseJSON(JSONParser.java:42)
As can be seen in the log messages, the IP filter is being invoked and is allowing the request to continue. However, the debug string that is being emitted in my handler is nowhere to be seen and the return body is blank. Can anyone tell me why my security filter would prevent MockMvc from having its request successfully routed to my controller?
Final Note: if I use the http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')"); configuration that I first listed or completely get rid of Spring Security by removing my SecurityConfig class, the request is routed successfully to my handler.
I figured out how to make the test work. I was not able to find a single article that answered my question but by taking different suggestions from multiple blog posts, I came up with this which works for me:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HandlerController.class)
public class HandlerControllerTest {
private MockMvc mvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
// mvc = MockMvcBuilders.standaloneSetup(new HandlerController()).build();
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getIndex() throws Exception {
mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
}
#Test
public void getMessageService() throws Exception {
mvc.perform(get("/OutboundMessageService").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"status\": \"SUCCESS\"}", true));
}
}
As you can see, I am no longer auto-wiring the MockMvc object and allowing it be automatically set up but am instead setting it up myself in the setUp() method. The commented-out line in the setUp() method works to successfully test my controller also but it doesn't route the request through my Spring Security IP address filter. I'm leaving it in so that users that don't need to test Spring Security can see an alternate method to set up the MockMvc object. The uncommented line sets up a MockMvc object such that it runs the request through both the security filters and my controller.
I know it is late but for others looking for the answer, you can add that filter to the MockMvc object like this:
#Autowired
private MockMvc mvc;
#Autowired
private YourCustomFilter filter;
#Before
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(filter).build();
}