With Spring MVC can you make one controller call another controller to handle an error page? - spring

I have a controller which can throw an exception. When it does throw an exception it will show my errorpage.jsp, instead of the jsp page I wanted to show. An exxample of this controller is:
public class myController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> model = new HashMap<>();
// Some logic which can throw an error....
return model;
}
}
The application-servlet.xml looks like this:
...
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/login.html">myController</prop>
<prop key="/errorpage.html">myErrorController</prop>
</props>
</property>
</bean>
...
Normally when an error, like navigating to a page that doesn't exist, occurs the error controller will be called which will return a hashmap of variables for the error page jsp. However, when the login controller encounters an error it will show the error page, but it won't call the error controller which would provide some of these parameters, so the page is incomplete.
Is there some way to define in the servlet.xml for the myController to call the myErrorController when it encounters an error and redirects to the error page?

You can annotate a controller class with #ControllerAdvice which will be shared between all Controllers. Then using the #ExceptionHandler annotation you can create methods to handle specific exceptions.
Example:
#ControllerAdvice
public class ExceptionHandlerAdvice {
#ExceptionHandler(MyException.class)
public ModelAndView handleException(MyException e, HttpServletRequest request, HttpServletResponse response) { // included servlet request and response
ModelAndView model = new ModelAndView("error/errorpage.jsp");
model.addObject("exception", e);
return model;
}
}
And anytime you throw MyException in any of your other controllers, this controller advice method will handle the exception for you! As the exception, servlet request and response is passed to the ExceptionHandler method, all of that information is available to you.
Example in another controller:
#Controller
public class SomeController {
#Autowired
private DataService dataService;
#RequestMapping(value = '/', method = RequestMethod.POST)
public String doSomething() {
return dataService.foobar(); // throws MyException which is handled with ExceptionHandler
}
}

Related

Spring Integration: ClientHttpRequestInterceptor intercept() method not called when request is sent

I have set up a spring-integration application as followed, and want to log the outbound request using ClientHttpRequestInterceptor.
The setup is below.
I sent the Http-get using curl and got result, but the method CustomClientHttpRequestInterceptor.intercept() never gets called when app is debugged.
I must have missed something in the setup. Thanks for helping.
Configuration:
<bean id="customRT" class="org.springframework.web.client.RestTemplate">
<property name="interceptors">
<list>
<bean class="hello.CustomClientHttpRequestInterceptor" />
</list>
</property>
</bean>
<int:chain...>
<int:header-enricher....
<int-http:outbound-gateway url="http://example.com/vhosts"
rest-template="customRT"
http-method="GET"/>
</int:chain>
public class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
logger.info("request.getURI().toString());
return execution.execute( request, body );
}
The configuration and code are correct. I've forgot that there is a cache installed, and that's why the intercept() method never gets called.

Spring 2.5 Ajax 1.7, updater receive wrong response

I am using Spring 2.5, and use Ajax.updater (1.7) to get an response from an requestmapping in Spring.
In the request mapping I am returning an ModelAndView which refers to an jsp with some text. But the problem is that when ajax received the response and update the container, the entire page is wrapped in the container instead just the jsp output, why is this
Ok, this is how I used ajax with Spring 2.5
First of all you need controller to respond on ajax calls
public class AjaxController extends MultiActionController {
// injected services, daos here
public ModelAndView getAjaxData(HttpServletRequest request, HttpServletResponse response) throws Exception {
String data = // construct your ajax response here - string, json, xml etc
model.put("data", data);
return new ModelAndView("ajax_foo", model);
}
public ModelAndView anotherMethod(HttpServletRequest request, HttpServletResponse response) throws Exception {
String data = // construct your ajax response here - string, json, xml etc
model.put("data", data);
return new ModelAndView("ajax_foo", model);
}
}
Then you need ajaxView to write ajax data to response
public class AjaxView extends AbstractView {
#Override
protected void renderMergedOutputModel(Map map, HttpServletRequest request, HttpServletResponse response) throws Exception {
String ajaxResponse = map.get("data");
response.setContentType("text/plain; charset=UTF-8");
response.getOutputStream().write(ajaxResponse.getBytes());
}
View resolver to resolve ajax calls
public class AjaxViewResolver extends AbstractCachingViewResolver {
private String ajaxPrefix;
private View ajaxView;
#Override
protected View loadView(String viewName, Locale locale) throws Exception {
View view = null;
if (viewName.startsWith(this.ajaxPrefix)) {
view = ajaxView;
} else {
}
return view;
}
public void setAjaxPrefix(String ajaxPrefix) {
this.ajaxPrefix = ajaxPrefix;
}
public void setAjaxView(View ajaxView) {
this.ajaxView = ajaxView;
}
}
Url mapping, map ajax url to ajax controller
<bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/getAjaxData.htm">ajaxController</prop>
<prop key="/anotherMethod.htm">ajaxController</prop>
</props>
</property>
</bean>
Controller has bean ajaxResolver as methodNameResolver, controller has some services or daos for example
<bean name="ajaxController" class="my.test.web.ajax.AjaxController">
<property name="service" ref="service"/>
<property name="dao" ref="dao"/>
<property name="methodNameResolver" ref="ajaxResolver"/>
</bean>
Method name resolver
<bean id="ajaxResolver"
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/getAjaxData.htm">getAjaxData</prop>
<prop key="/anotherMethod.htm">anotherMethod</prop>
</props>
</property>
</bean>
Ajax View resolver to call your ajax view when you return modelAndView from AjaxController
<bean id="ajaxViewResolver" class="my.test.web.ajax.AjaxViewResolver">
<property name="ajaxView">
<bean class="my.test.web.ajax.AjaxView"/>
</property>
<property name="ajaxPrefix" value="ajax_"/>
</bean>

Upgrading to spring-3.1 seems to break my CustomWebArgumentResolver

I'm trying to upgrade a spring MVC app from 3.0.6 to 3.1.2 and some controllers that used to work don't seem to work anymore. I've read the spring docs, but I'm confused about what's compatible with what.
We've got a CustomWebArgumentResolver that looks for any request parameter named "asOf" and coverts its value to a date. We call it, unimaginatively, the "AsOfDateConverter." When upgrading to spring-3.1.2, I took advantage of the new namespace functionality and added this to my applicationContext:
<mvc:annotation-driven conversion-service="conversionService">
<mvc:argument-resolvers>
<bean id="customWebArgumentResolver" class="my.converters.CustomWebArgumentResolver">
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
The CustomWebArgumentResolver is straightforward:
public class CustomWebArgumentResolver implements WebArgumentResolver {
private AsOfDateConverter asOfDateConverter;
#Override
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
if (isAsOfDateParameter(methodParameter)) {
return asOfDateConverter.convert(webRequest.getParameter("asOf"));
}
return UNRESOLVED;
}
Then an example controller might look something like this:
#Controller
#Secured({BaseController.ROLE_LOGGED_IN})
#org.springframework.transaction.annotation.Transactional
public class DashboardController extends BaseController {
public static final String URL = "/dashboard";
#RequestMapping(value=URL, method=RequestMethod.GET)
public ModelAndView get(#RequestParam(required=false) String requestedMeterType, #AsOf Date asOf) {
debug(log, "Rendering dashboard asOf %s", asOf);
etc etc
The "asOf" parameter is coming in null, and I'm sure I'm missing something obvious. If anyone out there neck deep in the latest MVC 3.1 stuff could point me in the right direction I'd be grateful.
Thanks!
Tom
EDIT:
The AsOf annotation:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface AsOf {
}
More of my applicationContext:
<mvc:annotation-driven conversion-service="conversionService">
<mvc:argument-resolvers>
<bean class="[blah].AsOfDateHandlerMethodArgumentResolver">
<property name="asOfDateConverter">
<bean class="[blah].AsOfDateConverter"/>
</property>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
<!-- Added to re-support #Controller annotation scanning after upgrading to spring-3.1. -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="[blah].converters.CustomerConverter"/>
<bean class="[blah].converters.AccountConverter"/>
<bean class="[blah].converters.DateConverter"/>
<bean class="[blah].converters.CustomerCommunicationInstanceConverter"/>
<bean class="[blah].converters.MeterTypeConverter"/>
<bean class="[blah].converters.AreaAmountConverter" p:precision="0"/>
<bean class="[blah].converters.LengthAmountConverter" p:precision="1"/>
</set>
</property>
</bean>
The API has changed with Spring 3.1 - the interface to implement to resolve a controller argument is HandlerMethodArgumentResolver. You can continue to use CustomWebArgumentResolver, by adapting it to a HandlerMethodArgumentResolver
However changing the code to use HandlerMethodArgumentResolver also will be easy:
public class CustomWebArgumentResolver implements HandlerMethodArgumentResolver {
private AsOfDateConverter asOfDateConverter;
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
if (isAsOfDateParameter(methodParameter)) {
return asOfDateConverter.convert(webRequest.getParameter("asOf"));
}
return UNRESOLVED;
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return (methodParameter.getParameterAnnotation(AsOf.class)!=null)
}
Edit
After looking through your comments, I think I have an idea about what could be going wrong. Can you please check your #AsOf annotation, you probably have not declared the retention of Runtime, which could be the reason why the the WebArgumentResolver is not taking effect:
#Retention(RetentionPolicy.RUNTIME)
public #interface AsOf {
}
Anyway here is a gist with a full working test along the same lines:
https://gist.github.com/3703430

Spring dependency injection to other instance

from the app-context.xml:
<bean id="userDao" class="com.vaannila.dao.UserDAOImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<bean name="MyServiceT" class="com.s.server.ServiceT">
<property name="userDao" ref="userDao"/>
</bean>
and inside ServiceT.java:
private UserDAO userDao;
public void setUserDao(UserDAO userDao){
this.userDao = userDao;
}
the issue is: the setUserDao is called when the server goes on but when I call my doGet method:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.write("hello");
}
the userDao is null.
I put a breakpoint inside the setUserDao method and than another one inside the doGet method and saw that it is not the same insatnce... what is the reason? how can I fix it?
thanks!
Spring is atowiring your bean correctly, the problem is that servlet container instantiates your servlet independently of spring. So you basically have two different instances - one created by spring and another created by container.
One workaround is to use ServletContextAttributeExporter, by putting the following in your app-context.xml:
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="userDao">
<ref bean="userDao"/>
</entry>
</map>
</property>
and then, in your servlet:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
UserDao userDao = (UserDao)getServletContext().getAttribute("userDao");
// do something with userDao
PrintWriter writer = response.getWriter();
writer.write("hello");
}
another is to access the WebApplicationContext directly:
protected void doGet(HttpServletRequest reqest, HttpServletResponse response)
throws ServletException, IOException {
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
UserDao userDao =(UserDao)springContext.getBean("userDao");
}
... or simply use Spring MVC and let it autowire everything like it should.
Also see this blog post. It might be easier to convert your servlet to HttpRequestHandler and let it be served by HttpRequestHandlerServlet, both provided by spring.

Spring : Command object not getting populated

I have two controllers , a simple form controller and a multiaction controller.
Now, in simpleformcontroller, i want to redirect a request to multiaction controller.
Here's code snippet in simpleformcontroller
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) {
MyObject myOb = (MyObject )command;
system.out.println(myOb.toString);
ModelAndView mav = new ModelAndView(new RedirectView("another.htm"));
mav.addObject("Obj",myOb);
return mav;
}
another.htm binds to a method in multiaction controller.
<bean id="MyController" class="MyController">
<property name="methodNameResolver">
<bean class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/another.htm">another</prop>
</props>
</property>
</bean>
</bean>
and the code in multiactioncontroller is
public class MyController extends MultiActionController {
public ModelAndView another(HttpServletRequest request,
HttpServletResponse response, MyObject myObj) {
system.out.println(myObj.toString());
}
}
The output is, all fields of Myobj are nulls in mutiactioncontroller whereas they have valid values when passed in simpleformcontroller.
Am i missing something here or is this not the right way to pass command objects ?
Any help is appreciated.
Unless you store your MyObject in The session, you will always get null. It occurs because The user data does not survive redirect. To store your command object in The session, use setSessionForm in The SimpleFormController and Then retrieve your command by using
public ModelAndView another(HttpServletRequest request, HttpServletResponse response, HttpSession session, MyObject myObj) throws CRPMException {
instead

Resources