RequestDispatcher ending in infinite loop - servlets

I have two WAR applications and the mode of communication between them is via servlets.
My application (WAR A) opens a child window with the URL of a servlet in another WAR (lets say WAR B).
The servlet (in WAR B) processes the data and should send the processed data back to original application's servlet (i.e WAR A's servlet).
But this process ends in an infinite loop and also the URL parameters sent from WAR-A are null.
Here is the code snippet :
The below script opens a child window with the URL of servlet in WAR-B also passing some URL parameters.
function invokePlugin(invokeURL, custValJSON, meaCompPartJSON) {
window.open(invokeURL + '?custValJSON=' + custValJSON,'');
}
Below is servlet code in WAR-B which extracts the URL parameters and process the data and again send the request back to WAR-A's servlet...
private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String custValJSON = request.getParameter("custValJSON");
System.out.println("custValJSON : " + custValJSON);
CustomValues custVal = gson.fromJson(custValJSON, CustomValues.class);
if(custVal != null) {
System.out.println("Cust val details : " + custVal.getName());
custVal.setValue("Satya");
}
String destination = "/testPlannerPluginResult";
RequestDispatcher reqDispatch = request.getRequestDispatcher(destination);
request.setAttribute("custValJSON", gson.toJson(custVal));
if(reqDispatch != null) {
reqDispatch.forward(request, response);
}
}
Does anybody have idea on this?
Regards,
Satya

That then just means that the servlet is basically calling itself everytime. I don't immediately see the cause in the information given so far, but apparently the URL which you passed into the getRequestDispatcher() matches the URL of the servlet itself.
I however see a major mistake here:
RequestDispatcher reqDispatch = request.getRequestDispatcher(destination);
request.setAttribute("custValJSON", gson.toJson(custVal));
That can impossibly invoke the servlet which runs in another servlet context (read: another WAR). You need ServletContext#getContext() first to get the other servlet context and then use ServletContext#getRequestDispatcher() to dispatch the request to there.
ServletContext otherServletContext = getServletContext().getContext("/otherContextPath");
RequestDispatcher dispatcher = otherServletContext.getRequestDispatcher(destination);
This only requires that the both WARs are configured to expose the context for sharing. On Tomcat for example, this is to be done by adding crossContext="true" to the <Context>.

Related

AEM Servlet not getting executed

I have a servlet with OSGI annotation like below
#Component( immediate = true, service = Servlet.class, property = { "sling.servlet.extensions=json",
"sling.servlet.paths=/example/search", "sling.servlet.methods=get" } )
public class SearchSevrlet
extends SlingSafeMethodsServlet {
#Override
protected void doGet( final SlingHttpServletRequest req, final SlingHttpServletResponse resp )
throws ServletException, IOException {
log.info("This is not getting called ");
}
}
But When i try to hit the servlet with JQuery
$.get( "/example/search.json", function( data ) {
$( ".result" ).html( data );
alert( "Load was performed." );
});
I am getting below information rather than servlet getting executed.
{"sling:resourceSuperType":"sling/bundle/resource","servletClass":"com.group.aem.example.servlet.SearchSevrlet","sling:resourceType":"/example/search.servlet","servletName":"com.group.aem.example.servlet.SearchSevrlet"}
Please let me know if i need to make any other configuration.
The info that you are getting is the answer of the Default JSON Servlet
Please read this: Servlets and Scripts
You are registering the "SearchServlet" with the property "sling.servlet.paths". This property is defined as:
sling.servlet.paths: A list of absolute paths under which the servlet is accessible as a Resource. The property value must either be a single String, an array of Strings...
That means that your servlet will be only triggered if you request the same exact path, in this case "/example/search", like this:
GET /example/search
I would recommend you to use the properties "resourceTypes" and "selectors" in your Servlet rather than "paths". For example, a better configuration could be:
property = {
"sling.servlet.resourceTypes=/example/search.servlet",
"sling.servlet.selectors=searchselector",
"sling.servlet.extensions=json",
"sling.servlet.methods=GET"
}
With this config, your SearchServlet should be triggered with a GET request to a resource with resourceType="/example/search.servlet", with the selector "searchselector" and the extension "json". For example:
GET /corcoran/search.searchselector.json
I had a similar problem with yours.
To find out what is wrong, I checked "Recent Requests" page.
(at http://localhost:4502/system/console/requests.)
In my case, there was a log saying, "Will not look for a servlet at (my request path) as it is not in the list of allowed paths".
So I moved to "Config Manager" page(at http://localhost:4502/system/console/configMgr), and searched for "Apache Sling Servlet/Script Resolver and Error Handler".
It has a list named "Execution Paths", and I added my request path to the list.
After adding my path to the list, the problem is solved.

Access Denied when trying to forward the request in CQ

All, I'm trying to upload a file in dam in CQ using assestManager and then trying to set values in metadata. Then I'm retrieving all the data one by one and storing in a list, and set it to request object and pass it to new jsp page using 'rd.forward(request, response);' but I'm getting error as:
javax.jcr.AccessDeniedException: Access denied.
even though all the access are given.
Code:-
String redirect = request.getParameter(":redirect"); //content/nextgen/marine/podupload.html
RequestDispatcher rd = request.getRequestDispatcher(redirect);
rd.forward(request, response); // throws me error as access denied
I'm assuming your initial request is a POST?
If so, try the following:
SlingHttpServletRequest newRequest = new SlingHttpServletRequestWrapper(request) {
public String getMethod() {
return "GET";
}
};
newRequest.getRequestDispatcher("/content/nextgen/marine/podupload.html")
.forward(newRequest, response);
If this is a GET request that you are trying to forward then it's a permission issue. If this is a POS or PUT request then you will need a SlingHttpServletRequestWrapper to wrap and modify your request as a GET request forward.
This is simply because sling cannot forward POST requests.

How to pass new header to sendRedirect

I feel like this should be easy. I have an app where all I am trying to do is have a form page (index.jsp) that calls a servlet (CheckInfo.java) which sets a new header (myHeader) and redirects the user to another page (redirect.jsp). All of these files are on the same server. The index.jsp is sending the request just fine and CheckInfo is processing and redirecting, but myHeader is not showing up on redirect.jsp. I've read several posts talking about response.sendRedirect sends a 302 which doesn't pass headers and that I should use RequestDispatcher, but nothing seems to work. Is there no way to send headers from a servlet to a jsp?
Here is the servlet code:
response.setHeader("myHeader", "hey there");
response.sendRedirect("redirect.jsp");
I have also tried this:
response.setHeader("myHeader", "hey there");
RequestDispatcher view = request.getRequestDispatcher("redirect.jsp");
view.forward(request, response);
And I have this in redirect.jsp:
System.out.println(request.getHeader("myHeader"));
This does not print anything.
If the answer to my question is no... then I would settle for a way to set the header once I got back to the jsp. My reverse proxy is looking for a specific header to determine whether or not to perform an action. Obviously I tried response.addHeader() on redirect.jsp, but the page has already loaded at that point so that just made me feel dumb.
response.setHeader("myHeader", "hey there");
response.sendRedirect("redirect.jsp");
You are adding it as response header and it is 302 response. Browser on seeing a 302 response will just look for Location header and fire a new request to this location. Custom headers in the response are untouched whereas you are expecting these custom response headers to be included in the request (to new redirect location) which is not being sent.
Solution:-
1. you can use request dispatcher and forward the request instead of external redirect. And you need to use request attributes here.
2. you can call submit form using an ajax request may be jquery like and handle the response manually(for 302 response) but would not suggest you to use this approach as it is not a cleaner and intuitive approach. Just mentioning so that you know there are other ways to achieve this.
The problem is that the redirect() method of the response initiates a new request altogether, thereby loosing the attributes that were set before redirecting. Luckily there is a fluent way of solving the problem still. See below
response.setHeader("myHeader", "hey there");
request.getRequestDispatcher("redirect.jsp").forward(request, response);
Then in your destination you can do response.getHeaders("myHeader")
I have tested the code.
I hope it's clear that in case of asking the client to redirect to another URL - the browser shall not honor the cookies.
However, the 2nd method - where server forwards the request is feasible. The main mistake appears to be in mutating the response while we are supposed to change the request.
Then again, one cannot directly mutate a HttpServletRequest object. Here is one way to do so:
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request){
public String getHeader(String name) {
String value = super.getHeader(name);
if(Strings.isNullOrEmpty(value)) {
...
value = myNewHeader;
}
return value;
}
public Enumeration<String> getHeaders(String name) {
List<String> values = Collections.list(super.getHeaders(name));
if(values.size()==0) {
...
values.add(myNewHeader);
}
return Collections.enumeration(values);
}
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.add(myNewHeaderName);
...
return Collections.enumeration(names);
}
}
Followed by:
RequestDispatcher view = request.getRequestDispatcher("redirect.jsp");
// OR (If you can get servletContext)
RequestDispatcher view = servletContext.getRequestDispatcher("redirect.jsp");
view.forward(requestWrapper, response);
Reference:
https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequestWrapper.html
For the headers case - getHeader(), getHeaders() and getHeaderNames() fn in the reqWrapper obj need Overriding.
Similarly you can override cookies and params.
See also: Modify request parameter with servlet filter
NOTE: It might not be possible to forward a req to an endpoint which expects a different MIME type.
A client side redirect creates a new HTTP request/response pair.
This link may help you more on debugging perspective -
Sending Custom headers

How to prevent static resources from being handled by front controller servlet which is mapped on /*

I have a servlet which acts as a front controller.
#WebServlet("/*")
However, this also handles CSS and image files. How can I prevent this?
You have 2 options:
Use a more specific URL pattern such as /app/* or *.do and then let all your page requests match this URL pattern. See also Design Patterns web based applications
The same as 1, but you want to hide the servlet mapping from the request URL; you should then put all static resources in a common folder such as /static or /resources and create a filter which checks if the request URL doesn't match it and then forward to the servlet. Here's an example which assumes that your controller servlet is a #WebServlet("/app/*") and that the filter is a #WebFilter("/*") and that all your static resources are in /resources folder.
HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());
if (path.startsWith("/resources/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + path).forward(request, response); // Goes to your controller.
}
See also How to access static resources when mapping a global front controller servlet on /*.
I know this is an old question and I guess #BalusC 's answer probably works fine. But I couldn't modify the URL for the JSF app am working on, so I simply just check for the path and return if it is to static resources:
String path = request.getRequestURI().substring(request.getContextPath().length());
if (path.contains("/resources/")) {
return;
}
This works fine for me.

Adding a servlet filter that wraps the request into a HttpServletRequestWrapper seem to break page forwading

I'm having issues getting servlet forwarding to work when the request have been wrapped by a HttpServletRequestWrapper.
This is a server-side equinox application, running from within Eclipse using the embedded Jetty web server to provide the HttpService implementation.
To isolate the issue, the only thing my field does is to wrap the request and pass it forward:
public class TestFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest)request), response);
}
...
}
...and my servlet does nothing but trying to forward to a JSP:
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher("/account.jsp");
try {
dispatcher.forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
According to the servlet 2.3 spec, the request and response objects passed to the forward method can be either the original ones, or an instance of the provided wrappers. I need to pass a custom wrapper, but when I do that, the forward fails the following exception:
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.String.substring(Unknown Source)
at org.eclipse.equinox.http.servlet.internal.ProxyServlet.service(ProxyServlet.java:64)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.eclipse.equinox.http.jetty.internal.HttpServerManager$InternalHttpServiceServlet.service(HttpServerManager.java:318)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:390)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:327)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
at org.eclipse.equinox.http.servlet.internal.RequestDispatcherAdaptor.forward(RequestDispatcherAdaptor.java:30)
at mypackage.internal.signup.PlatformSetupServlet.doPost(PlatformSetupServlet.java:53)
...
If I take the wrapper away and pass forward the original request the servlet forward succeeds.
This looks like a bug in the org.eclipse.equinox.http.servlet implementation, but I might be missing something.
Am I doing something wrong?
Thank you!!
Eduardo Born
looks like the ProxyServlet is running into problems when parsing the Request's information.
Found the source of ProxyServlet:
http://kickjava.com/src/org/eclipse/equinox/http/servlet/internal/ProxyServlet.java.htm
Though it doesn't match exactly, you can see from the code that it's trying to extract path info from your request -- and it didn't find a "/", which it was expecting. You can try tweaking the path of the servlet maybe, just to test it out?
This should be caused by the code in ProxyServlet.service method, passing -1 to substring.
But the reason "forward" is breaking your code, is that "forward" doesn't create a whole new wrapper hierarchy. Instead, it finds and replaces the innermost request with the new forwarded one.
So the outer wrapper doesn't change, and if you ask it to return the queryString for instance, if it has a cached query string, it will return the same one, not the forwarded one.
I'm having an issue with this, which I'm trying to resolve. Will post here again if I ever succeed.
See this question that I just posted for a better description of what I mean:
How to know when the request is forwarded in a RequestWrapper object

Resources