How to get Request object in CustomPhaseListener? - servlets

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

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.

Spring 4 upgrade broke error page filter chain

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.

How can I redirect but keep the URL the same

I am doing the following, but the url in the address bar changes. from /test to localhost:8080...
Is it possible to keep the url the same in the address bar?
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>xxx.xxxx.Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Servlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getRequestURI();
response.sendRedirect("http://localhost:8080"+path);
}
You can use forward instead of redirect. I wrote a method that gets a servlet's name and dispatch it:
protected void gotoServlet(HttpServletRequest req, HttpServletResponse resp,String servletName) throws ServletException, IOException {
RequestDispatcher dispatcher = this.getServletContext().getNamedDispatcher(servletName);
dispatcher.forward(req,resp);
}
You have to first understand that your Servlets (HttpServlet) and the Servlet container they're running in are implementing an HTTP stack. HTTP is a request-response protocol.
The client sends an HTTP request and the server (your Servlet container) replies with an HTTP response. In this case,
response.sendRedirect("http://localhost:8080"+path);
it is responding with a 302, indicating redirection. How your client handles this is up to them. Typically, a browser client will send a new HTTP GET request to the redirection target. This will force a page refresh/renew.
If that's not the behavior you want, you need to change your client behavior. For example, you can put part of your client logic within an iframe. You'd then have the redirect only refresh the iframe.

Servlet + redirection

I need some suggestions. I have defined servlet mapping as
<servlet-mapping>
<servlet-name>My Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
However there are some static html files. So i have mapped them to the default servlet to serve the static html files
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
However, i want the user to have access to them only when the user has logged in. The welcome page is Login.html. If the user tries to access any other static file and has not logged in i.e there is not session then i should redirect user to the login page. But with current mapping the user is able to access index.html file as the request is served by default servlet.
Please suggest .
Your intent is to have a front controller servlet, not to replace the default servlet. So you should actually not be mapping your front controller servlet on /.
You should map the controller servlet on a more specific URL pattern, such as /app/*. To keep URLs transparent, your best bet is to create a filter which determines the request URI and continues the chain on static content like HTML and dispatches the remnant to the controller servlet.
E.g.
String uri = request.getRequestURI();
if (uri.endsWith(".html")) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("/app" + uri).forward(request, response);
}
You can extend the DefaultServlet of your web server.The extended servlet will be your front controller. In the doGET or doPOST method forward your static pages to the super class. DefaultServlet is the servlet that is mapped to url "/" by default. I have used it with jetty server but it can be implemented in tomcat as well.
public class FrontController extends DefaultServlet {
#Override
public void init() throws UnavailableException {
super.init();
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
/*
* if request is trying to access inside /static then use the default
* servlet. YOU CAN USE YOUR OWN BUSINESS LOGIC TO FORWARD REQUESTS
* TO DEFAULTSERVLET
*/
if (uri.startsWith("/static/")) {
super.doGet(request, response);
return;
} else {
// else use your custom action handlers
}
}
}
In the above code samples I have forwarded all the requests starting with /static/ to the default servlet to process. In this way you can map the FrontController to "/" level .
<servlet>
<description></description>
<display-name>FrontController</display-name>
<servlet-name>FrontController</servlet-name>
<servlet-class>FrontController</servlet-class>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>/</url-pattern>

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