I am trying to test the routing I have configured on my http inbound channel. The test is working if I have the following test code:
#Test
public void success() throws JsonProcessingException, ServletException, IOException, Exception {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(createRequest());
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
request.setRequestURI("/service/agent/onbreak");
request.setContent(json.getBytes());
request.setContentType("application/json");
Map<String, String> pathVars = new AntPathMatcher().extractUriTemplateVariables(
"/service/agent/{function}", "/service/agent/onbreak");
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathVars);
MockHttpServletResponse response = new MockHttpServletResponse();
inboundGateway.handleRequest(request, response);
Assert.assertEquals(HttpStatus.OK.value(), response.getStatus());
verify(mockAgentService, times(1)).onBreak(any(AgentOnBreakRequest.class));
}
My issue with the above method is that I need to apply set the pathVars on the mock request. I have been trying unsuccessfully to use the MockMvc classes. I was at the point where I could submit the json payload successfully however it is not being passed on to the http inbound channel adapter.
Spring config:
<int-http:inbound-channel-adapter channel="http.incoming.agent.request.channel" supported-methods="POST"
path="service/agent/{function}">
<int-http:header name="function" expression="#pathVariables.function"/>
</int-http:inbound-channel-adapter>
<int:router input-channel="http.incoming.agent.request.channel" expression="headers.function">
<int:mapping value="onbreak" channel="asl.agent.convert.on.break" />
</int:router>
<int:json-to-object-transformer input-channel="asl.agent.convert.on.break"
output-channel="asl.agent.request.channel"
type="agent.dto.AgentOnBreakRequest"/>
<int:payload-type-router input-channel="asl.agent.request.channel">
<int:mapping channel="request.asl.agent.on.break" type="com.mhgad.za.vitel.server.agent.dto.AgentOnBreakRequest"/>
</int:payload-type-router>
<int:service-activator input-channel="request.asl.agent.on.break" output-channel="asl.agent.response.channel"
ref="agentService" method="onBreak" />
<bean id="agentService" class="service.agent.impl.AgentServiceImpl"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler default-servlet-name="AgentService"/>
<mvc:view-controller path="/*" view-name="initialView" />
Test code:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("test/resources/webapp")
#ContextConfiguration(locations = {"classpath:spring/agent-integration.xml", "classpath:spring/agent-beans.xml",
"classpath:spring/test-router-config.xml"})
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class AgentOnBreakRouteTest {
#Autowired
private WebApplicationContext webCtx;
#Test
public void successful() throws JsonProcessingException, Exception {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(createRequest());
MockMvc mvc = webAppContextSetup(webCtx).build();
mvc.perform(post("service/agent/onbreak").content(json)).andExpect(status().isOk());
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<servlet>
<servlet-name>aslAgentGateway</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>aslAgentGateway</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
log:
08:55:25.121 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - DispatcherServlet with name '' processing POST request for [service/agent/onbreak]
08:55:25.124 [main] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path service/agent/onbreak
08:55:25.124 [main] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [service/agent/onbreak]
08:55:25.124 [main] DEBUG o.s.i.h.i.IntegrationRequestMappingHandlerMapping - Looking up handler method for path service/agent/onbreak
08:55:25.125 [main] DEBUG o.s.i.h.i.IntegrationRequestMappingHandlerMapping - Did not find handler method for [service/agent/onbreak]
08:55:25.125 [main] WARN o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI [service/agent/onbreak] in DispatcherServlet with name ''
08:55:25.126 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request
Judging from the log, the dispatcher cannot route the request to the inbound channel. Can anyone advise me on how to accomplish this? I have looked through various examples and postings but have come up short so far.
Related
Find below code I have created.
I want to log(AOP Advice before,after etc.) all controller methods,
but advise apply only one controller (AuthenticationController) methods is executed, on second controller (UserManagerController) methods advise are not apply,both controller are in the same package(com.framework.controller),but I don't know whats go wrong?why its apply only one controller not other.
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Dispatcher Servlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
applicationContext.xml
<!-- Used to activate applied annotations in already registered beans in application context -->
<!-- Declares support for general annotations such as #Required, #Autowired, #PostConstruct, and so on -->
<context:annotation-config />
<context:component-scan base-package="com.framework.*"/>
<!-- ***************************************************************** -->
<!-- Declares explicit support for annotation-driven MVC controllers (i.e. #RequestMapping, #Controller, #RequestBody/ResponseBody) -->
<mvc:annotation-driven />
<!-- Logging Aspect -->
<bean id="frameworkLoggingAspect" class="com.framework.aspect.FrameworkLoggingAspect" />
<!-- Enable #AspectJ annotation support -->
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="frameworkLoggingAspect" />
</aop:aspectj-autoproxy>
Find my Aspect class
FrameworkLoggingAspect.java
#Aspect
public class FrameworkLoggingAspect {
#Pointcut("execution(* com.framework.controller.*.*(..))")
public void beforeControllerMethod() {
}
#Pointcut("execution(* com.framework.controller.*.*(..))")
public void afterControllerMethod() {
}
#Before("beforeControllerMethod()")
public void logBeforeAllMethods(JoinPoint joinPoint) {
CustomLogger.info(" Before Name " + joinPoint.getSignature().getName());
CustomLogger.info(" Before Modifier " + joinPoint.getSignature().getModifiers());
}
#After("afterControllerMethod()")
public void logAfterAllMethods(JoinPoint joinPoint) {
CustomLogger.info(" After Name " + joinPoint.getSignature().getName());
CustomLogger.info(" After Modifier " + joinPoint.getSignature().getModifiers());
}}
Find Controller package(com.framework.controller)
AuthenticationController.java
#Controller
#RequestMapping("/authentication")
public class AuthenticationController {
#Autowired
AuthenticationService authenticationService;
#RequestMapping(value = "loginPage", method = RequestMethod.GET)
public ModelAndView loginPage(HttpServletRequest request, HttpServletResponse response) {
ModelMap map = new ModelMap();
CustomLogger.info("Inside Login Page");
return new ModelAndView("user.login", map);
}
#RequestMapping(value = "logout", method = RequestMethod.GET)
public ModelAndView loginOut(HttpServletRequest request, HttpServletResponse response) {
ModelMap map = new ModelMap();
CustomLogger.info("Inside logout Page");
return new ModelAndView("user.login", map);
}
#RequestMapping(value = "validateUser", method = RequestMethod.POST)
public ModelAndView validateUser(HttpServletRequest request, HttpServletResponse response) {
ModelMap map = new ModelMap();
CustomLogger.info("Inside validate user Page");
return new ModelAndView("user.dashboard", map);
}
}
UserManagerController.java
#Controller
#RequestMapping("/userManager")
public class UserManagerController {
#Autowired
UserManagerService userManagerService;
#RequestMapping(value = "view", method = RequestMethod.GET)
ModelAndView view(HttpServletRequest request, HttpServletResponse response) {
ModelMap map = new ModelMap();
CustomLogger.info("Inside User View Page");
return new ModelAndView("user.manager.view", map);
}
#RequestMapping(value = "saveUser", method = RequestMethod.POST)
ModelAndView saveUser(HttpServletRequest request, HttpServletResponse response) {
ModelMap map = new ModelMap();
CustomLogger.info("Inside Save User");
return new ModelAndView("user.manager.view", map);
}}
Find below output i am getting
below controller(FrameworkLoggingAspect) advise applying properly
advise before for method loginPage
INFO 2017-10-03 13:32:40,426 [http-nio-8080-exec-10] - com.framework.aspect.FrameworkLoggingAspect : Before Name loginPage
INFO 2017-10-03 13:32:40,426 [http-nio-8080-exec-10] - com.framework.aspect.FrameworkLoggingAspect : Before Modifier 1
INFO 2017-10-03 13:32:40,426 [http-nio-8080-exec-10] - com.framework.controller.AuthenticationController : Inside Login Page
advise after for method loginPage
INFO 2017-10-03 13:32:40,427 [http-nio-8080-exec-10] - com.framework.aspect.FrameworkLoggingAspect : After Name loginPage
INFO 2017-10-03 13:32:40,427 [http-nio-8080-exec-10] - com.framework.aspect.FrameworkLoggingAspect : After Modifier 1
advise before for method validateUser
INFO 2017-10-03 13:33:15,292 [http-nio-8080-exec-6] - com.framework.aspect.FrameworkLoggingAspect : Before Name validateUser
INFO 2017-10-03 13:33:15,293 [http-nio-8080-exec-6] - com.framework.aspect.FrameworkLoggingAspect : Before Modifier 1
INFO 2017-10-03 13:33:15,293 [http-nio-8080-exec-6] - com.framework.controller.AuthenticationController : Inside validate user Page
advise after for method validateUser
INFO 2017-10-03 13:33:15,294 [http-nio-8080-exec-6] - com.framework.aspect.FrameworkLoggingAspect : After Name validateUser
INFO 2017-10-03 13:33:15,294 [http-nio-8080-exec-6] - com.framework.aspect.FrameworkLoggingAspect : After Modifier 1
but in this controller(UserManagerController) advise not applying
INFO 2017-10-03 13:34:18,467 [http-nio-8080-exec-9] - com.framework.controller.UserManagerController : Inside User View Page
Please help me with this, I know this is silly question and already have lot of tutorial posted for that, but believe me I already go throw it but still I cant understand whats go wrong ?
The methods in UserManagerController are package-visible. They might need to be public (depending on your Spring version). For CGLib, Spring 3.2.18 recommends public method signatures, whereas Spring 5 can intercept public and protected methods (and, according to the note in the documentation, "even package-visible methods if necessary").
I'm using Spring MVC to create my project template.
I'm trying to send a JSON request to my spring controller using Ajax, I tried by adding the content type of json etc. Its a POST request. however the trials got failed.
index.jsp
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$.ajax({
url:"getNames",
type:"POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({"name":"testName"}),
async: false,
cache: false,
dataType : "json",
processData:false,
success: function(resposeJsonObject){
// Success Action
}
});
});
</script>
Controller
My total controller other than imports
#Controller
public class HelloWorldController {
// Produces JSON data
#RequestMapping(value="hello",produces="application/json")
public #ResponseBody String helloWorld() throws JSONException {
JSONObject obj = new JSONObject();
obj.put("my key", "my Value");
return obj.toString();
}
// Accept JSON data
#RequestMapping(value="getNames", method=RequestMethod.POST)
public #ResponseBody Test addNewWorker(#RequestBody Test jsonString) {
Test test = new Test();
test.setName(jsonString.getName());
System.out.println(test.getName());
return test;
}
Web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>Spring3MVC</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
My Test class
public class Test implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My configuration file of spring
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="net.viralpatel.spring3.controller" />
<bean id="test" class="com.qiib.spring3.model.Test"></bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Error
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver logException
WARNING: Handler execution resulted in exception: Content type 'application/json;charset=UTF-8' not supported
Following JAR's are available in my project
aopalliance-1.0.jar
commons-logging-1.2.jar
jackson-core-asl-1.9.13.jar
jackson-databind-2.1.4.jar
jackson-mapper-asl-1.9.13.jar
jackson-servlet-api-3.1.0.jar
jstl-1.2.jar
spring-aop-4.2.0.RELEASE.jar
spring-beans- ""
spring-context-""
spring-core-""
spring-expression-""
spring-web-""
spring-webmvc-4.2.0.RELEASE.jar
I've imported your code into mine project and it works absolutely fine. You should check your beans and pom.xml. I'm posting my code and result:
Controller:
#RequestMapping(value="getNames", method=RequestMethod.POST)
public #ResponseBody Test addNewWorker(#RequestBody Test jsonString) {
Test test = new Test();
test.setName(jsonString.getName());
System.out.println(test.getName());
return test;
}
index.js:
$.ajax({
url:"getNames",
type:"POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({"name":"testName"}),
async: false,
cache: false,
dataType : "json",
processData:false,
success: function(resposeJsonObject){
// Success Action
}
});
output:
2017-08-17 08:59:22.045 INFO 7092 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-08-17 08:59:22.046 INFO 7092 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2017-08-17 08:59:22.049 INFO 7092 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 3 ms
testName
Not an expert in developing APIs so need some help/advice from the community.
I have an application that used Java Spring MVC for APIs. I am using ResponseEntity to return responses back to anyone who would use the APIs (third-party or UI).
Example of my API
#Controller
#RequestMapping("/Test/")
public ResponseEntity<TestGroup> getTestsById(#PathVariable("id") Integer id) {
TestGroup testGroup = testService.getTestById(id); //calls a service that returns test from the db
if(testGroup != null) {
return new ResponseEntity(testGroup, HttpStatus.OK);
} else {
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
}
I have other APIs similar to this. My question is whether there are any frameworks or ways so that I can make my APIs return JSON response for errors or success in a well formatted JSON. Example
{
"code": 400,
"message": "Bad Request",
"description": "There were no credentials found."
}
{
"code": 200,
"data": "{<JSON blob of the object to be returned>}"
}
Also,
Have implemented a Filter to check for session information (think of them as oauth tokes) for authentication before any of the routes are mapped. I need this process of error handling to work at that stage too, so that if there is an expired token or invalid token I get back a well formatted JSON. Example
{
"code": 401,
"message": "Unauthorized",
"description": "The token used in the request is incorrect or has expired."
}
Right now I get the default message Spring's authentication Filter throws in the form of an HTML
<html><head><title>Apache Tomcat/7.0.50 - Error report</title><style><!--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;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 500 - Access is denied</h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>message</b> <u>Access is denied</u></p><p><b>description</b> <u>The server encountered an internal error that prevented it from fulfilling this request.</u></p><p><b>exception</b> <pre>org.springframework.security.access.AccessDeniedException: Access is denied
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
com.test.security.CookieAuthenticationFilter.doFilter(CookieAuthenticationFilter.java:95)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
</pre></p><p><b>note</b> <u>The full stack trace of the root cause is available in the Apache Tomcat/7.0.50 logs.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.50</h3></body></html>
Apologize for not communicating this, I am not using spring security for the APIs and my web.xml for the filter looks like this
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Webapp</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.spring.config</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.spring.config=</param-value>
</init-param>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Here is how the security configuration is handled.
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private AbstractConfiguration configurationManager;
public SecurityConfiguration() {
super(true);
}
#Override
#Bean
protected AuthenticationManager authenticationManager() {
return new CustomAuthenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/health/**").permitAll().and().authorizeRequests()
.antMatchers("/complete/**").permitAll().and()
.addFilterBefore(cookieAuthenticationFilter(), ChannelProcessingFilter.class).authorizeRequests()
.antMatchers("/**").hasAuthority("tests");
}
#Bean
public GenericFilterBean cookieAuthenticationFilter() {
if (System.getProperty("noauth") != null) {
return new SecurityFilterMock();
} else {
return new CookieAuthenticationFilter(redisTemplate());
}
}
}
As per suggestions if you read below, I have made the following EntryPoint class to handle the token authentication and display a JSON
public class TokenAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private static final Logger log = LoggerFactory.getLogger(TokenAuthenticationEntryPoint.class);
#Override
public void commence(HttpServletRequest request,HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.info(String.format("Unauthorized access with session id '%s'",
request.getSession().getId()));
ErrorHandler errorResponse = new ErrorHandler();
// populate your response object with all the info you need
errorResponse.setCode(401);
errorResponse.setDescription("The token used in the request is incorrect or invalid.");
errorResponse.setMessage("Unauthorized");
ObjectMapper jsonMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
PrintWriter out = response.getWriter();
out.print(jsonMapper.writeValueAsString(errorResponse));
}
}
Any suggestions or directions would be really appreciated
To return JSON formatted errors to authentication with Spring you can have a look at my answer here: How to return JSON response for unauthorized AJAX calls instead of login page as AJAX response?
Basically, you'll have to tweak authentication mechanism and redefine the entry point to properly hendle the response.
I thought this would be pretty straightforward, but apparently I'm missing something, and I can't seem to figure out what it is.
I'm trying to add a servlet to an existing web application written using java and spring. Here's what I did:
I added the following to web.xml:
<servlet>
<servlet-name>SettingServlet</servlet-name>
<display-name>SettingServlet</display-name>
<description>Provides a rest endpoint for getting and setting settings.</description>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/SettingServlet-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SettingServlet</servlet-name>
<url-pattern>/setting/*</url-pattern>
</servlet-mapping>
Then I created the following file (.../WEB-INF/SettingServlet-servlet.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
>
<bean id="settingController"
class="com.my.package.path.SettingController"
p:settingService-ref="settingService"/>
</beans>
Then I created the following Controller (com.my.package.path.SettingController.java):
#Controller
#RequestMapping("/setting")
public class SettingController {
private SettingService settingService;
public void setSettingService(final SettingService settingService) {
Validate.notNull(settingService, "SettingController::settingService cannot be null");
this.settingService = settingService;
}
#ResponseBody
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
#Secured({ "ROLE_ADMINISTRATOR", "ROLE_CURIOUS_GEORGE" })
public ResponseEntity<String> getSettingRequest(#PathVariable("name") final String name, #RequestParam("setting_family") final String settingFamily) {
final String jsonBody = "{\"setting\":\"" + name + "\", \"Setting Family\":\"" + settingFamily + "\", \"value\":\"test\"}";
return new ResponseEntity<String>(jsonBody, HttpStatus.OK);
}
}
WHAT AM I DOING WRONG? :( I get a 404 not found exception when trying to do a GET request at /setting/fruit?setting_family=foods
as you declared url pattern to your dispatcher as "/setting/*" and then in controller you declared "/setting" as a root requestMapping for this controller, you should be able to access your controller using this url: /setting/setting/fruit?setting_family=foods
:)
I am using spring mvc 3.2.4 and jquery 1.9.0 for long polling. My application is deployed on Tomcat 7.0.42. My spring configuration files are as below:
Application Web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee"
version="3.0">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/app-servlet.xml
</param-value>
</context-param>
</web-app>
Spring Configration xml as:-
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:annotation-config/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<context:component-scan base-package="com.webchat"/>
<bean id="defferedResult" class="com.exp.DeferredResultContainer"></bean>
</beans>
Controller for Posting data looks as
#RequestMapping(value = "/postComment", method = RequestMethod.POST)
public #ResponseBody String postComment(HttpServletRequest request) {
deferredResultContainer.updateAllResults(request.getParameter("comment"));
return "success";
}
Deferred Result container class
public class DeferredResultContainer {
private final Set<DeferredResult<String>> deferredResults = Collections.synchronizedSet(new HashSet<DeferredResult<String>>() );
public void put(DeferredResult<String> deferredResult){
deferredResults.add(deferredResult);
}
public void updateAllResults(String value){
for (DeferredResult<String> deferredResult : deferredResults){
deferredResult.setResult(value);
}
}
public void remove(DeferredResult<String> deferredResult){
deferredResults.remove(deferredResult);
}
public int determineSize(){
return deferredResults.size();
}
}
Controller for Deferred Result looks as
#RequestMapping(value = "/getComments", method = RequestMethod.GET)
#ResponseBody
public DeferredResult<String> getComments() throws Exception{
final DeferredResult<String> deferredResult= new DeferredResult<String>();
deferredResultContainer.put(deferredResult);
deferredResult.onTimeout(new Runnable() {
#Override public void run() {
deferredResultContainer.remove(deferredResult);
}
});
deferredResult.onCompletion(new Runnable() {
#Override public void run() {
deferredResultContainer.remove(deferredResult);
}
});
return deferredResult;
}
When I am trying to long poll it through ajax i am getting following response :-
{"setOrExpired":false}
And onCompletion method is also not getting executed.
To Simply things below controller gives perfect response as
{"1":"2"}
#RequestMapping(value = "/test1", method = RequestMethod.GET)
#ResponseBody
public Map test1() throws Exception{
Map m1 = new HashMap<String, Object>();
m1.put("1", "2");
return m1;
}
Once i change it to below and add Deferred result i get response as
{"setOrExpired":true}
#RequestMapping(value = "/test", method = RequestMethod.GET)
#ResponseBody
public DeferredResult<Map> test() throws Exception{
DeferredResult<Map> result = new DeferredResult<Map>();
Map m1 = new HashMap<String, Object>();
m1.put("1", "2");
result.setResult(m1);
return result;
}
Polling code
$(document).ready(function() {
longPoll();
function longPoll(){
$.support.cors = true;
var path = "http://localhost:8080/WebChatExp/rest";
$.ajax({
url: path + "/getComments",
cache:false,
success: function(data){
//To Do
alert("Data" + JSON.stringify(data));
},
error: function(err, status, errorThrown ) {
},
type: "GET",
dataType: "json",
complete: longPoll,
timeout: 60000 // timeout every one minute
});
}
I have searched various examples but cannot figure out if any extra configuration is required for deferred result. Please advise.
The response body you are getting
{"setOrExpired":true}
indicates that Spring serialized your DeferredResult (which has various properties including setOrExpired) to JSON instead of handling it with a DeferredResultMethodReturnValueHandler. In other words, it used another HandlerMethodReturnValueHandler, most likely RequestResponseBodyMethodProcessor (which handles #ResponseBody), to handle the value returned from your #RequestMapping annotated handler method. (The simplest way to test this is to see what happens when you remove the #ResponseBody annotation.)
Looking at the 3.2.x source code of RequestMappingHandlerAdapter, which registers the default HandlerMethodReturnValueHandler instances, the DeferredResultMethodReturnValueHandler is registered before RequestResponseBodyMethodProcessor and therefore will handle the DeferredResult return value first.
Since you're seeing different behavior, we must assume your configuration is not what you've shown here. (Note that <mvc:annotation-driven/> registers a RequestMappingHandlerAdapter.)
Also note that you are currently loading the configuration in /WEB-INF/app-servlet.xml twice, once by the ContextLoaderListener and once by the DispatcherServlet.
Get rid of your ContextLoaderListener and the corresponding context-param entirely. They are not needed in your example.
I know is impossible to be the problem in this case (spring mvc 3.2.4) but just for future references:
I get the same issue with spring boot 2.2.0.BUILD-SNAPSHOT (spring 5.1.5).
This error happens when you try to return DeferredResult using webflux instead of the traditional spring mvc app. Remember, in the new webflux api you should use mono/flux, not DeferredResult.