Suppose my Tomcat webapps directory looks like this:
webapps/
webapps/fooapp/
webapps/fooapp/WEB-INF/
webapps/fooapp/WEB-INF/web.xml
webapps/fooapp/bardir/
When I make a GET request for /fooapp/bardir, Tomcat sees that webapps/fooapp/bardir is a directory and sends back a 302 to /fooapp/bardir/ (with a slash at the end).
Here is my question: Where in the Tomcat source code does this take place? (I'm looking at 6.0.x but a correct answer for any version would be a great starting point.)
The only reference material I can find on this subject is in the Catalina Functional Specifications which states, regarding the Default Servlet:
On each HTTP GET request processed by this servlet, the following processing shall be performed:
[...]
If the requested resource is a directory:
If the request path does not end with "/", redirect to a corresponding path with "/" appended so that relative references in welcome files are resolved correctly.
However, this functionality does not appear to be in org.apache.catalina.servlets.DefaultServlet; or at least, it's not there exclusively: if I replace the default servlet in web.xml with a servlet whose servlet-class does not exist, directory paths still come back 302 to add the slash, while every other request comes back with an error as expected.
I think it happens in org.apache.tomcat.util.http.mapper.Mapper, namely in the internalMapWrapper (Context, CharChunk, MappingData) method.
But unfortunately I'm not really sure -- maybe this really is a question better suited for the tomcat-users mailing list. Sorry for not having a better answer.
The Eclipse debugger learnt me that the redirect happens in line 504 of CoyoteAdapter class, almost in the end of the postParseRequest() method.
// Possible redirect
MessageBytes redirectPathMB = request.getMappingData().redirectPath;
if (!redirectPathMB.isNull()) {
// ...
response.sendRedirect(redirectPath); // <--- Here.
return false;
}
Tomcat 6.0.20 btw.
Update: actually, the redirectPath is indeed filled by the Mapper as mentioned in #Henning's answer, indeed in the internalMapWrapper() method. Checkout the source code here.
if(mappingData.wrapper == null && noServletPath) {
// The path is empty, redirect to "/"
mappingData.redirectPath.setChars
(path.getBuffer(), pathOffset, pathEnd);
path.setEnd(pathEnd - 1);
return;
}
Related
I'm using Spring Boot 2.4.6. For delete APIs getting 405 method not found. My endpoint is like: beauty/v1/sites/addressTemplates/:templateId
Path variable: ##$%#
Can someone please suggest what can be done to make this behavior as not complaining for 405? Please direct me to other questions in case I'm missing something.
I guess that your issue has nothing to do with Spring. Maybe you are trying to compose the whole URL by using reserved characters.
In a URL, a hash mark, number sign, or pound sign ( # ) points a browser to a specific spot in a page or website. It is used to separate the URI of an object from a fragment identifier. Source.
Which means that an URL which looks like:
beauty/v1/sites/addressTemplates/##$%#
is not exactly what you imagine it to be because # is interpreted in a special way. What you have to do is to percent encode the "special" path variable so it will look like this at the end:
beauty/v1/sites/addressTemplates/%23%40%24%25%23
Then Spring will not complain anymore and will resolve properly the endpoint.
I'm trying to get my Spring 4 application to allow Path Parameters that end with an escaped slash.
The #RequestMapping I've got is:
#RequestMapping("/{externalSystemId}/{externalRequestId}/events")
And the URL that I'm calling with is /dummy/ab%2F/events. So "externalRequestId" is the one that has the trailing encoded slash.
I've gotten to the point where the handler is called correctly, and where escaped slashes in the middle of the path segment work - e.g. /dummy/ab%2Fcd/events, but not at the end.
What I've done so far:
Added system properties to Tomcat:
-Dorg.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true
-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
Custom extension of UrlPathHelper that replaces getServletPath with one that simply returns request.getRequestURI()
Configure the UrlPathHelper with:
urlDecode = false
removeSemicolonContent = false
Use this UrlPathHelper from mvc:path-matching, within mvc:annotation-driven
And I'm at a complete loss as to where to go next. I've even been debugging through both Spring and Tomcat to try and work it out, and I'm stumped. It looks like it's because UrlPathHelper.getPathWithinServletMapping() is calling request.getPathInfo(), and that is returning the path with the %2F stripped out. And that in turn is because somewhere else in Tomcat - I've lost the window now - it has decoded the %2F into a /, then replaced the "//" that is now present with a "/" instead.
This feels like it should be relatively straightforward, so what am I missing?
I've tried it on both Tomcat 7 and Tomcat 8, and with various versions of Spring 4 - currently on 4.3.14.
Problem solved.
The SlashFriendlyUrlPathHelper that I had that was the extension of UrlPathHelper was causing problems, because my webapp is not the Root webapp. Remove that, but keep every other change in place, and it starts to work properly.
I'm attempting to do a reverse lookup on a route I've created.
The route is defined as such in my routes file:
POST /login controllers.Web.Application.authenticate
However, when I try to do a reverse on it in a form I made, I can't seem to find it. I'm attempting to do a reverse like this:
#helper.form(action = routes.login())) {
rest of the form here...
}
However, the login appears in red in intellij and when attempting to run the program, I get the following error:
play.sbt.PlayExceptions$CompilationException: Compilation error[value login is not a member of object controllers.routes]
I've attempted recompiling the project multiple times and looking around the docs, but it's just not working... Any ideas?
So, an interesting thing happened recently and I found a way to point to the original structure properly. To do so, rather than writing:
routes.Web.Application.authenticate()
As Tyler suggested, I was supposed to write:
Web.routes.Application.authenticate()
Which is totally counter-intuitive and all, but that allows me to use the original package structure.
Edit:
As we discovered in the comments, the reverse router doesn't seem to like sub packages. You should remove the Web part of your package, and move everything up a level, then change the code to be this:
#helper.form(action = routes.Application.authenticate())) {
rest of the form here...
}
Original answer:
You need to refer to the controller function, and not the name of the route, so it should look something like this:
#helper.form(action = routes.Web.Application.authenticate())) {
rest of the form here...
}
I am currently migrating an Eclipse 3.0 application to 4.4. The user data was and still should be stored in the folder C:\Users\username\AppData\Roaming\applicationname
The application is using following code to read the directory:
public static String getUserDirectory()
{
String directory = InternalPlatform.getDefault().getUserLocation().getFile();
return directory;
}
I know the code is deprecated, but following code returns the same:
public static String getUserDirectory()
{
String directory = Platform.getUserLocation().getURL().getFile();
return directory;
}
They both return C:\Users\username\user but as I said the user data should be stored at C:\Users\username\AppData\Roaming\applicationname. Did the behaviour of those methods change?
How can I realize that I store my user data under C:\Users\username\AppData\Roaming\applicationname and my application can still find the directory?
I know this has to do something with environment-variables which I don't fully understand.
I don't have a 3.x target platform at hand to compare but C:\Users\username\user looks plain wrong.
If you are interested in the details, the constructor of EquinoxLocations computes the userLocation and adds the literal 'user' the the user's home directory if no default is specified.
Hence, if you start your application with -user #user.home or -Dosgi.user.area=#user.home, the user location will be set to C:\Users\username\. Still not what you are looking for, but at least a sane value.
I think this is a bug in Equinox and recommend to file a bugzilla. If it turns out that there is a good reason for this appraoch the bug entry will still serve as documentation/reasoning.
In the meanwhile you could obtain the home directory on Windows through System.getenv( "APPDATA" ). According to this post it will return the roaming home directory.
I solved the problem by adding three properties in the Configuration tab of my config.ini.product-file:
osgi.configuration.area =
#user.home/AppData/Roaming/ApplicationName/...
osgi.user.area =
#user.home/AppData/Roaming/ApplicationName/...
osgi.instance.area =
#user.home/AppData/Roaming/ApplicationName
Now my method as stated in my question reads the paths that are configured by those properties and the config.ini file which is generated looks exactly like the one of the old build with Eclipse 3.0.
I have a JSP that takes an Arraylist from the session object and removes the items from it. It seemed to be working fine and then out of nowhere when I navigate to that page, the page is blank. I checked the Tomcat log files and in catalina.out I am receiving a JasperException an it is showing it as being on a line with the following
for(int i; i < agentItems.size(); i++)
agentItems is the name of the ArrayList I am using. I have been debugging it and can't seem to figure out what the problem might be. I have read that a JasperException is sometiems thrown as a JSP's NullPointerException. Is this true or am I just completely overlooking the problem?
I have the web application running on a local machine and a intermediate server for development in which both of them have had no trouble. Why could it be that only on this server it is giving me problems?
That can be everything. You need to look a bit further in the stacktrace, peek to the caused by or root cause part and the trace which comes thereafter. It can be caused by many things. The JSP basically get compiled into one large try block and any catched Throwable will be wrapped into a servletcontainer specific exception like JasperException in Tomcat and clones. It boils down to this:
try {
// All translated JSP code comes here. Max 64K.
} catch (Throwable t) {
throw new JasperException(t);
}
Check the .java filename in the 1st line of the stacktrace, locate this in the work directory of the servletcontainer and open the file in an editor. Do you see it?
That said, using scriptlets is a bad practice. Use Servlets to control/preprocess/postprocess requests, use Javabeans to represent data models, use Taglibs in JSP to control the page flow and output, use Expression Language (EL) in JSP to access backend data. In your specific case, you can loop over an array or List using JSTL's c:forEach tag.
<c:forEach items="${agents}" var="agent">
<p>Agent: ${agent.name}
</c:forEach>
jasperexception can also occur when JSP is not able to access an element or item it's obtaining from outside and here it maybe is unable to access agentItems. Try to provide fully qualified class name or import the package otherwise.