Spring 4 upgrade broke error page filter chain - spring-mvc

Scenario:
We have an interceptor that looks for bogus attributes in URLs and throws a NoSuchRequestHandlingMethodException if it finds one. We then display a custom 404 page.
All pages go through the same filter chain to set up the local request state, log some information, and then display the requested page. In Spring 4, it stopped going through the filter chain for the 404 page in this case. It still goes through it if you go to a completely bogus page, and the 404 works, but when we throw the NoSuchRequestHandlingMethodException, the filters don't happen.
Spring 3:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. New filter chain starts
5. We log the error page metrics
6. We display a nice 404 page to the customer
Spring 4:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. We try to log the error page metrics, but NPE since a second filter chain never started
5. We display a terrible blank page to the customer
Filter code in web.xml:
<!-- The filter that captures the HttpServletRequest and HttpServletResponse-->
<filter>
<filter-name>ServletObjectFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>xxxxxxx.servletObjectFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ServletObjectFilter</filter-name>
<servlet-name>springmvc</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
...
<error-page>
<error-code>404</error-code>
<location>/errors/404</location>
</error-page>
Filter code:
public void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain chain )
throws ServletException, IOException {
try {
getServletContainer().setServletObjects( request, response );
chain.doFilter( request, response );
} finally {
getServletContainer().removeAll();
}
ServletContainer:
static final ThreadLocal< HttpServletRequest > REQUESTS = new ThreadLocal< HttpServletRequest >();
static final ThreadLocal< HttpServletResponse > RESPONSES = new ThreadLocal< HttpServletResponse >();
public void setServletObjects( HttpServletRequest request, HttpServletResponse response ) {
REQUESTS.set( request );
RESPONSES.set( response );
}
public void removeAll() {
REQUESTS.remove();
RESPONSES.remove();
}
Code that then fails:
public class RequestResponseAwareBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization( Object bean, String beanName ) {
...
if ( bean instanceof RequestAware ) {
HttpServletRequest request = getServletContainer().getRequest();
if ( request == null ) {
throw new IllegalStateException( "The request object is NULL" );
}
RequestAware requestAware = (RequestAware) bean;
requestAware.setRequest( request );
}
}

I "solved" the problem by splitting up my error page #Controller into two, one where they're the targets of internal redirects and don't get the filter chain, and one where they are directly loaded, and do get the filter chain. I then added the redirect #Controller to the interceptor blacklist, so it doesn't require any logic or data from the filters. It solved this specific problem, but I'm worried that something else in my codebase also relies on this behavior.

Related

What happens when an exception is thrown from a filter after the servlet completes?

I am working on a service hosting several REST API using Java Servlet.
The Servlet uses annotations provided by javax.ws.rs to define the API's URL, HTTP method, etc. So one of them may look like as follows:
#Path("root")
#Service
public class MyClass {
#Path("path")
#Get
#Produces("application/json; charset=UTF-8")
public ResponseObject myAction() { ... }
}
I also have a filter that due to some bug, could throw an exception after executing the servlet. So it behaves as follows:
public class MyFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
chain.doFilter(request, response);
throws new Exception();
}
}
The web.xml contains following definition: (not complete, just those I think might be relevant)
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>myorg.MyFilter</filter-class>
</filter>
When I call this API using different approaches, I get different results.
Using Jersey client and get as string
WebResource resource = client.resource("https://my-service/root/path");
ClientResponse response = resource.get(ClientResponse.class);
String s = response.getEntity(String.class);
This will give me below error:
Caused by: java.io.IOException: Premature EOF
at sun.net.www.http.ChunkedInputStream.readAheadBlocking(ChunkedInputStream.java:565)
at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:609)
at sun.net.www.http.ChunkedInputStream.read(ChunkedInputStream.java:696)
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3393)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.Reader.read(Reader.java:140)
at com.sun.jersey.core.util.ReaderWriter.readFromAsString(ReaderWriter.java:171)
at com.sun.jersey.core.util.ReaderWriter.readFromAsString(ReaderWriter.java:157)
at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.readFromAsString(AbstractMessageReaderWriterProvider.java:114)
at com.sun.jersey.core.impl.provider.entity.StringProvider.readFrom(StringProvider.java:73)
at com.sun.jersey.core.impl.provider.entity.StringProvider.readFrom(StringProvider.java:58)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:634)
Using Jersey client but de-serializing the JSON response into an object
WebResource resource = client.resource("https://my-service/root/path");
ClientResponse response = resource.get(ClientResponse.class);
ResponseObject result = response.getEntity(ResponseObject.class);
This will return successfully and the resulting ResponseObject contains correct content.
Call this API using HTTP tool Postman. This will also return status 200 and correct response body.
Call the API using .Net core library RestSharp
var restClient = new RestClient("https://my-service/");
var request = new RestRequest("/root/path", method);
var response = restClient.Execute(request);
This will fail with following error:
The server returned an invalid or unrecognized response.
at System.Net.Http.HttpConnection.FillAsync()
at System.Net.Http.HttpConnection.ChunkedEncodingReadStream.ReadAsyncCore(Memory`1 buffer, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.HttpContentReadStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at RestSharp.Extensions.MiscExtensions.ReadAsBytes(Stream input)
at RestSharp.Http.ProcessResponseStream(Stream webResponseStream, HttpResponse response)
at RestSharp.Http.ExtractResponseData(HttpResponse response, HttpWebResponse webResponse)
at RestSharp.Http.GetResponse(HttpWebRequest request)
My question is:
What happened to my HTTP response in this case and why different clients behaved differently ? I guess when MyClass.myAction completed, the serialized return object ResponseObject was already sent to the client. However, when the control was given back to MyFilter, an error was thrown. This must have had some impact on the HTTP response, but what ? And why would this by observed in some cases by some client but ignored by others ?
It's quite easy for me to fix the bug itself - to not throw exception in the filter. But I want to understand more about what happened underneath and appreciate any help.

Servlet WebServlet multiple urlPattern

I must write a servlet that should do multiple operations according to the button I press on the html page.
I have one button that is a Insert into the Db, another is a Delete, another is a Select.
So I would like my servlet will do all those three operations in the doGet method.
My clue was to put
#WebServlet("select", "insert", "delete")
public class MyServlet extends HttpServlet {
...
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getServletPath()!=null?request.getServletPath():"";
switch(path){
case "/select":
...
break;
switch(path){
case "/insert":
...
break;
switch(path){
case "/delete":
...
break;
then, into the javascript file it would have been (for select)
var req={
method: 'GET',
url: 'http://localhost:8080/AngularDb/select',
}
$http(req);
and into the web.xml
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
But all this doesn't work.
May you tell me what is wrong?

How to get Request object in CustomPhaseListener?

Am hitting my Servlet from a link. Some Cookies would have been already set in Client. When my Servlet is hit, I want to retrieve these Cookies.
For eg., am hitting the link like http:/myDomain/myServlet/ServletReceiver
In web.xml, I have below code
<servlet>
<display-name>ServletReceiver</display-name>
<servlet-name>ServletReceiver</servlet-name>
<servlet-class>(location of my ServletReceiver)</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletReceiver</servlet-name>
<url-pattern>/ServletReceiver</url-pattern>
</servlet-mapping>
And my ServletReceiver code is below
public class ServletReceiver extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
// Do some checks here based on cookies obtained and redirect to corresponding page
RequestDispatcher dispatcher=request.getRequestDispatcher("/pages/index.jsf");
dispatcher.forward(request, response);
}
}
My requirement is, when I retrieve some data from Cookies, I want to set it into bean. And since am creating an instance of the bean in CustomPhaselistener (and not in ServletReceiver), if I get the request object through which I can get cookie values then I can set that in my bean in PhaseListener.
My bean is request scoped.
So, is there a way to get request object in CustomPhaseListener?
Also, am retrieving Cookies in doGetmethod. Is that suggested?
Am using JSF 1.2

web.xml error-page location redirect is not going through my filter definitions

In my web.xml I've done the following error-page mappings, but when they are invoked those invoked requests are not passing through the filter definitions specified in web.xml file.
<error-page>
<error-code>403</error-code>
<location>/error.vm?id=403</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/error.vm?id=400</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.vm?id=404</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/servlet-exception.vm</location>
</error-page>
My application is using spring-mvc and I want to handle the handler not found condition from spring mvc. My application is an multi tenant application where some filters are responsible for setting some information related to the schema.
The requests are reaching in my error.vm controller but since they are passing through the filter I'm not able to determine the theme and SecurityContext etc.
How to solve this problem?
Thank you.
Instead of using web.xml's error pages you could use a servlet filter. The servlet filter could be used to catch all exceptions, or just a particular exception such as org.springframework.web.portlet.NoHandlerFoundException. (Is that what you mean by "handler not found" exception?)
The filter would look something like this:
package com.example;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.web.portlet.NoHandlerFoundException;
public class ErrorHandlingFilter implements Filter {
public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
// Or you could catch Exception, Error, Throwable...
// You probably want to add exception logging code here.
// Putting the exception into request scope so it can be used by the error handling page
request.setAttribute("exception", e);
// You probably want to add exception logging code here.
request.getRequestDispatcher("/WEB-INF/view/servlet-exception.vm").forward(request, response);
}
}
}
Then, set this up in web.xml with the help of Spring's DelegatingFilterProxy:
<filter>
<filter-name>errorHandlingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And then finally, turn the filter into a spring bean inside your spring context xml:
<bean id="errorHandlingFilter" class="com.example.ErrorHandlingFilter" />
You might have to experiment with the order of the filter in the filter chain so that failed requests still go through the other filters you mentioned. If you're having trouble with that, a variation would be to do an HTTP redirect instead of a forward, like this:
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
request.getSession().setAttribute("exception", e);
response.sendRedirect("/servlet-exception.vm");
}
That would force the browser to request your error handling page as a new http request, which might make it easier to ensure it goes through all of the right filters first. If you need the original exception object, then you could put it in the session instead of the request.
maybe
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Seam and ServletOutputStream - flush is not immediately visible

I am converting a 6 year old application to Seam 2.2.
The application is used to run in java 1.4 and weblogic 8.
It only uses jsp and servlet.
In one servlet I use:
public void doGet (HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException
{
//...
ServletOutputStream out = = res.getOutputStream();
// displaying a lot of messages
// after each println() I do a flush()
out.println("lots of messages.....");
out.flush();
out.close();
//...
}
When running the application the messages were immediately seen in the browser.
When I run this using Seam 2.2 in Weblogic 10 and Java 1.6 the messages are not immediately seen in the browser.
Only when the servlet is finished running.
Can I change something to fix this?
I do not want to change/convert the servlet into a Seam component. The servlet is running fine. The only thing is the flushing of messages to the browser window which only happens after the servlet has stopped running.
Could it be that the reason is that the servlet now goes through the Seam filter:
<filter>
<filter-name>Seam Filter</filter-name>
<filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Seam Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The reason is probably that the request goes through the SeamFilter, as you supposed.
I think it's not the SeamFilter itself that buffer the data stream from your servlet but the Ajax4Jsf filter that is invoked in the filter chain.
If you have RichFaces in the classpath there is a seam component that registers the Ajax4jsf filter in the chain. Namely, the Seam component is org.jboss.seam.web.ajax4jsfFilter.
If you don't need RichFaces try removing it from the classpath. If you need it, I suggest that you override org.jboss.seam.web.ajax4jsfFilter in order to skip the Ajax4Jsf filter for requests directed to your servlet.
Another possible solution is converting your servlet in a filter as a Seam component (see #Filter annotation) and positioning it at the beginning of the chain with the around attribute. Something like:
#Name("FormerServlet")
#Scope(STATELESS)
#BypassInterceptors
#Filter(around = "org.jboss.seam.web.ajax4jsfFilterInstantiator")
public class FormerServletFilter implements Filter
{
protected void init(FilterConfig filterConfig) throws Exception
{
}
protected void doDestroy()
{
}
/**
* Performs the filtering for a request.
*/
protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain) throws Exception
{
if (thisRequestShoudBeManagedByMyServlet(request) )
{
// do here what you previously did in the servlet
} else
{
// go ahead with the Seam lifecycle
chain.doFilter(request, response);
}
}
You're running a servlet - there's nothing to do with Seam here. I suspect you need to re-evaluate your design, as there's not really an exact translation from servlet to Seam structure.

Resources