ajax call to java servlet results in 404 [duplicate] - java

This question already has answers here:
HTTP Status 404 - Servlet [ServletName] is not available
(4 answers)
Closed last year.
I'm trying to get a web page to send JSON data to a java servlet via a jQuery ajax POST.
I've already checked everything I could think of, but I still can't figure out why I keep getting a 404.
Even more confusing is that other calls to the same context path work correctly.
My web.xml
<web-app>
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>com.vibridi.klyr.servlet.Controller</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>CustomerServlet</servlet-name>
<servlet-class>com.vibridi.klyr.servlet.CustomerServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>/klyr</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>/home</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CustomerServlet</servlet-name>
<url-pattern>/klyr/customer/*</url-pattern>
</servlet-mapping>
My ajax call:
$.ajax({
url: "customer/save",
type: "POST",
data: JSON.stringify(o),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(obj) {
alert('Customer saved');
},
error: function(obj) {
alert('Error!');
}
});
My servlet:
public class CustomerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger("KLYR_LOGGER");
private CustomerManager manager;
public void init(ServletConfig sconfig) throws ServletException {
super.init(sconfig);
manager = new CustomerManager();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//stuff
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
response.setContentType("application/json;charset=utf-8");
try {
StringBuffer sb = new StringBuffer();
String line = null;
BufferedReader reader = request.getReader();
while((line = reader.readLine()) != null) {
sb.append(line);
}
manager.saveCustomer(sb.toString());
} catch(Exception e) {
logger.log(Level.SEVERE, "Data processing failure: " + e.getMessage());
out.write(Convertor.createBaseJSON(JSONType.E).toString());
out.close();
}
out.write(Convertor.createBaseJSON(JSONType.S).toString());
out.close();
}}
}
I can see from the Chrome's debugger tools that the call is properly directed to http://localhost:8080/klyr/customer/save but it 404's, whereas http://localhost:8080/klyr does not.
Thanks a lot!
EDIT:
I've tried to switch the servlet mappings over, i.e. /klyr (the working one) on CustomerServlet and /customer/save on Controller, but nothing happens, in fact when I call /klyr from the browser bar instead of seeing the response from CustomerServlet.doGet I still see the welcome page as if Controller.doGet fired. It looks like tomcat isn't reloading the web.xml file even if I restart it. Any ideas?

This is obvious because your CustomerServlet does not bind to $.ajax({url: "customer/save", ... so it won't work, your should change the below code :
<servlet-mapping>
<servlet-name>CustomerServlet</servlet-name>
<url-pattern>/klyr/customer/*</url-pattern>
</servlet-mapping>
to something like:
<servlet-mapping>
<servlet-name>CustomerServlet</servlet-name>
<url-pattern>/customer/save</url-pattern>
</servlet-mapping>
in order to solve the problem ~

I've eventually found the culprit, gonna post it here as a reference for other people.
Both servlets mapped in my web.xml are loaded on startup.
The first servlet attempted to read a config file from an incorrect path inside its init() method, but couldn't find it and threw an exception. The Catalina startup routine exited before it could load the second servlet, hence the 404 error.

Related

AngularJS posting data to servlet not working

I'm trying to post data from Angular to my servlet. But, it throws me the error
"Failed to load resource: the server responded with a status of 405 (Method Not Allowed)"
Here is my code. Am I missing anything?
$scope.pushDataToServer = function() {
$scope.data = {user_id:"123",key_name:"key2",value:"value2"};
$http({
method: 'POST',
url: 'pushData',
headers: {'Content-Type': 'application/json'},
data: $scope.data
}).success(function (data){
$scope.status=data;
}).error(function(data, status, headers, config) {
alert("error")
});
};
My servlet config
<servlet>
<servlet-name>pushData</servlet-name>
<servlet-class>com.data.pushData</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pushData</servlet-name>
<url-pattern>/pushData</url-pattern>
</servlet-mapping>
write a do post method in your servlet.
/**
* handles HTTP POST request
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
//TODO: handle POST here
}
Simple, the servlet you are calling doesn't support the POST method.
You haven't implemented it or has done so erroneously.

Getting null While Passing String from jsp to servlet

This is my Jsp File
<body>
<%
URL url = new
URL("http://localhost:8080/ServletToCloud/JSPToServletToCloudServlet");
URLConnection conn =url.openConnection();
conn.setDoOutput(true);
BufferedWriter bw= new BufferedWriter( new OutputStreamWriter(
conn.getOutputStream() ) );
bw.write("username= Shanx");
out.flush();
out.close();
%>
</body>
This is my servlet class
#SuppressWarnings("serial")
public class JSPToServletToCloudServlet extends HttpServlet
{
private final static String _USERNAME = "username";
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
String username = request.getParameter(_USERNAME);
response.setContentType("text/html");
out.println("HelloWorld");
out.println("Hello " + username);
out.close();
}
This is the web.xml file
<servlet>
<servlet-name>JSPToServletToCloud</servlet-name>
<servlet-class>pack.exp.JSPToServletToCloudServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>JSPToServletToCloud</servlet-name>
<url-pattern>/jsptoservlettocloud</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
My Jsp File is in Dynamic Web Application and is sending a string to Servlet which is in Web application Project. I am running the dynamic web application project on apache tomcat server and after the server is started I am running my web application projewct as web application and checking on local host and getting null.
Help me out guys.
you will have to put username name wither in session, because you are not set the username in request.
put user name in session and on server Side write :-
HttpSession session = request.getSession();
String username = session.getAttribute("username")

Internal Server Error In Web Application Project

I have a Dynamic application in which I have a JSP file which is sending a string to a Servlet which is in another project which is a Web application Project. I am using Tomcat server in JSP project and the server is starting just fine but when I am trying to run the web application on local host I am getting HTTP ERROR 500
Problem accessing /jsptoservlettocloud. Reason: INTERNAL_SERVER_ERROR
This is my JSP FILE
<body>
<%
String str= "Shanx";
URL u = new
URL("http://localhost:8080/ServletToCloud/JSPToServletToCloudServlet");
HttpURLConnection huc = (HttpURLConnection)u.openConnection();
huc.setRequestMethod("GET");
huc.setDoOutput(true);
ObjectOutputStream objOut = new ObjectOutputStream(huc.getOutputStream());
objOut.writeObject(str);
objOut.flush();
objOut.close();
%>
</body>
This is my Servlet Class
public class JSPToServletToCloudServlet extends HttpServlet
{
String str;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
try
{
str= (String) ois.readObject();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
finally
{
ois.close();
}
System.out.println("Servlet received : " + str);
}
}
This is my Web.xml file
<servlet>
<servlet-name>JSPToServletToCloud</servlet-name>
<servlet-class>pack.exp.JSPToServletToCloudServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>JSPToServletToCloud</servlet-name>
<url-pattern>/jsptoservlettocloud</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
In the url ServletToCloud is the name of the web application project and JSPToServletToCloudServlet is the name of the servlet. Is this url correct.
In your jsp you have http://localhost:8080/ServletToCloud/JSPToServletToCloudServlet but you have /jsptoservlettocloud in your web.xml
Your mapping is wrong.

Configure tomcat to accept post request

How can I configure tomcat so when a post request is made the request parameters are outputted to a jsp file? Do I need a servlet which forwards to a jsp or can this be handled within a jsp file ?
Here is my method which sends the post request to the tomcat server -
public void sendContentUsingPost() throws IOException {
HttpConnection httpConn = null;
String url = "http://LOCALHOST:8080/services/getdata";
// InputStream is = null;
OutputStream os = null;
try {
// Open an HTTP Connection object
httpConn = (HttpConnection)Connector.open(url);
// Setup HTTP Request to POST
httpConn.setRequestMethod(HttpConnection.POST);
httpConn.setRequestProperty("User-Agent",
"Profile/MIDP-1.0 Confirguration/CLDC-1.0");
httpConn.setRequestProperty("Accept_Language","en-US");
//Content-Type is must to pass parameters in POST Request
httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// This function retrieves the information of this connection
getConnectionInformation(httpConn);
String params;
params = "?id=test&data=testdata";
System.out.println("Writing "+params);
// httpConn.setRequestProperty( "Content-Length", String.valueOf(params.length()));
os = httpConn.openOutputStream();
os.write(params.getBytes());
} finally {
if(os != null)
os.close();
if(httpConn != null)
httpConn.close();
}
}
Thanks
First of all, your query string is invalid.
params = "?id=test&data=testdata";
It should have been
params = "id=test&data=testdata";
The ? is only valid when you concatenate it to the request URL as a GET query string. You should not use it when you want to write it as POST request body.
Said that, if this service is not supposed to return HTML (e.g. plaintext, JSON, XML, CSV, etc), then use a servlet. Here's an example which emits plaintext.
String id = request.getParameter("id");
String data = request.getParameter("data");
response.setContentType("text/plain");
response.setContentEncoding("UTF-8");
response.getWriter().write(id + "," + data);
If this service is supposed to return HTML, then use JSP. Change the URL to point to the JSP's one.
String url = "http://LOCALHOST:8080/services/getdata.jsp";
And then add the following to the JSP template to print the request parameters.
${param.id}
${param.data}
Either way, you should be able to get the result (the response body) by reading the URLConnection#getInputStream().
See also:
How to use URLConnection to fire and handle HTTP requests?
Unrelated to the concrete problem, you are not taking character encoding carefully into account. I strongly recommend to do so. See also the above link for detailed examples.
A servlet can handle both get and post request in following manner:
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//remaning usedefinecode
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
If you have a tomcat installation from scratch, don't forget to add the following lines to web.xml in order to let the server accept GET, POST, etc. request:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
...
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
...
</servlet>

Servlet for serving static content

I deploy a webapp on two different containers (Tomcat and Jetty), but their default servlets for serving the static content have a different way of handling the URL structure I want to use (details).
I am therefore looking to include a small servlet in the webapp to serve its own static content (images, CSS, etc.). The servlet should have the following properties:
No external dependencies
Simple and reliable
Support for If-Modified-Since header (i.e. custom getLastModified method)
(Optional) support for gzip encoding, etags,...
Is such a servlet available somewhere? The closest I can find is example 4-10 from the servlet book.
Update: The URL structure I want to use - in case you are wondering - is simply:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
So all requests should be passed to the main servlet, unless they are for the static path. The problem is that Tomcat's default servlet does not take the ServletPath into account (so it looks for the static files in the main folder), while Jetty does (so it looks in the static folder).
I came up with a slightly different solution. It's a bit hack-ish, but here is the mapping:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>myAppServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
This basically just maps all content files by extension to the default servlet, and everything else to "myAppServlet".
It works in both Jetty and Tomcat.
There is no need for completely custom implementation of the default servlet in this case, you can use this simple servlet to wrap request to the container's implementation:
package com.example;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class DefaultWrapperServlet extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
RequestDispatcher rd = getServletContext().getNamedDispatcher("default");
HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
public String getServletPath() { return ""; }
};
rd.forward(wrapped, resp);
}
}
I've had good results with FileServlet, as it supports pretty much all of HTTP (etags, chunking, etc.).
Abstract template for a static resource servlet
Partly based on this blog from 2007, here's a modernized and highly reusable abstract template for a servlet which properly deals with caching, ETag, If-None-Match and If-Modified-Since (but no Gzip and Range support; just to keep it simple; Gzip could be done with a filter or via container configuration).
public abstract class StaticResourceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final long ONE_SECOND_IN_MILLIS = TimeUnit.SECONDS.toMillis(1);
private static final String ETAG_HEADER = "W/\"%s-%s\"";
private static final String CONTENT_DISPOSITION_HEADER = "inline;filename=\"%1$s\"; filename*=UTF-8''%1$s";
public static final long DEFAULT_EXPIRE_TIME_IN_MILLIS = TimeUnit.DAYS.toMillis(30);
public static final int DEFAULT_STREAM_BUFFER_SIZE = 102400;
#Override
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException ,IOException {
doRequest(request, response, true);
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doRequest(request, response, false);
}
private void doRequest(HttpServletRequest request, HttpServletResponse response, boolean head) throws IOException {
response.reset();
StaticResource resource;
try {
resource = getStaticResource(request);
}
catch (IllegalArgumentException e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if (resource == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
String fileName = URLEncoder.encode(resource.getFileName(), StandardCharsets.UTF_8.name());
boolean notModified = setCacheHeaders(request, response, fileName, resource.getLastModified());
if (notModified) {
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
setContentHeaders(response, fileName, resource.getContentLength());
if (head) {
return;
}
writeContent(response, resource);
}
/**
* Returns the static resource associated with the given HTTP servlet request. This returns <code>null</code> when
* the resource does actually not exist. The servlet will then return a HTTP 404 error.
* #param request The involved HTTP servlet request.
* #return The static resource associated with the given HTTP servlet request.
* #throws IllegalArgumentException When the request is mangled in such way that it's not recognizable as a valid
* static resource request. The servlet will then return a HTTP 400 error.
*/
protected abstract StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException;
private boolean setCacheHeaders(HttpServletRequest request, HttpServletResponse response, String fileName, long lastModified) {
String eTag = String.format(ETAG_HEADER, fileName, lastModified);
response.setHeader("ETag", eTag);
response.setDateHeader("Last-Modified", lastModified);
response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_IN_MILLIS);
return notModified(request, eTag, lastModified);
}
private boolean notModified(HttpServletRequest request, String eTag, long lastModified) {
String ifNoneMatch = request.getHeader("If-None-Match");
if (ifNoneMatch != null) {
String[] matches = ifNoneMatch.split("\\s*,\\s*");
Arrays.sort(matches);
return (Arrays.binarySearch(matches, eTag) > -1 || Arrays.binarySearch(matches, "*") > -1);
}
else {
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
return (ifModifiedSince + ONE_SECOND_IN_MILLIS > lastModified); // That second is because the header is in seconds, not millis.
}
}
private void setContentHeaders(HttpServletResponse response, String fileName, long contentLength) {
response.setHeader("Content-Type", getServletContext().getMimeType(fileName));
response.setHeader("Content-Disposition", String.format(CONTENT_DISPOSITION_HEADER, fileName));
if (contentLength != -1) {
response.setHeader("Content-Length", String.valueOf(contentLength));
}
}
private void writeContent(HttpServletResponse response, StaticResource resource) throws IOException {
try (
ReadableByteChannel inputChannel = Channels.newChannel(resource.getInputStream());
WritableByteChannel outputChannel = Channels.newChannel(response.getOutputStream());
) {
ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_STREAM_BUFFER_SIZE);
long size = 0;
while (inputChannel.read(buffer) != -1) {
buffer.flip();
size += outputChannel.write(buffer);
buffer.clear();
}
if (resource.getContentLength() == -1 && !response.isCommitted()) {
response.setHeader("Content-Length", String.valueOf(size));
}
}
}
}
Use it together with the below interface representing a static resource.
interface StaticResource {
/**
* Returns the file name of the resource. This must be unique across all static resources. If any, the file
* extension will be used to determine the content type being set. If the container doesn't recognize the
* extension, then you can always register it as <code><mime-type></code> in <code>web.xml</code>.
* #return The file name of the resource.
*/
public String getFileName();
/**
* Returns the last modified timestamp of the resource in milliseconds.
* #return The last modified timestamp of the resource in milliseconds.
*/
public long getLastModified();
/**
* Returns the content length of the resource. This returns <code>-1</code> if the content length is unknown.
* In that case, the container will automatically switch to chunked encoding if the response is already
* committed after streaming. The file download progress may be unknown.
* #return The content length of the resource.
*/
public long getContentLength();
/**
* Returns the input stream with the content of the resource. This method will be called only once by the
* servlet, and only when the resource actually needs to be streamed, so lazy loading is not necessary.
* #return The input stream with the content of the resource.
* #throws IOException When something fails at I/O level.
*/
public InputStream getInputStream() throws IOException;
}
All you need is just extending from the given abstract servlet and implementing the getStaticResource() method according the javadoc.
Concrete example serving from file system:
Here's a concrete example which serves it via an URL like /files/foo.ext from the local disk file system:
#WebServlet("/files/*")
public class FileSystemResourceServlet extends StaticResourceServlet {
private File folder;
#Override
public void init() throws ServletException {
folder = new File("/path/to/the/folder");
}
#Override
protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
String pathInfo = request.getPathInfo();
if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
throw new IllegalArgumentException();
}
String name = URLDecoder.decode(pathInfo.substring(1), StandardCharsets.UTF_8.name());
final File file = new File(folder, Paths.get(name).getFileName().toString());
return !file.exists() ? null : new StaticResource() {
#Override
public long getLastModified() {
return file.lastModified();
}
#Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(file);
}
#Override
public String getFileName() {
return file.getName();
}
#Override
public long getContentLength() {
return file.length();
}
};
}
}
Concrete example serving from database:
Here's a concrete example which serves it via an URL like /files/foo.ext from the database via an EJB service call which returns your entity having a byte[] content property:
#WebServlet("/files/*")
public class YourEntityResourceServlet extends StaticResourceServlet {
#EJB
private YourEntityService yourEntityService;
#Override
protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
String pathInfo = request.getPathInfo();
if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
throw new IllegalArgumentException();
}
String name = URLDecoder.decode(pathInfo.substring(1), StandardCharsets.UTF_8.name());
final YourEntity yourEntity = yourEntityService.getByName(name);
return (yourEntity == null) ? null : new StaticResource() {
#Override
public long getLastModified() {
return yourEntity.getLastModified();
}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(yourEntityService.getContentById(yourEntity.getId()));
}
#Override
public String getFileName() {
return yourEntity.getName();
}
#Override
public long getContentLength() {
return yourEntity.getContentLength();
}
};
}
}
I ended up rolling my own StaticServlet. It supports If-Modified-Since, gzip encoding and it should be able to serve static files from war-files as well. It is not very difficult code, but it is not entirely trivial either.
The code is available: StaticServlet.java. Feel free to comment.
Update: Khurram asks about the ServletUtils class which is referenced in StaticServlet. It is simply a class with auxiliary methods that I used for my project. The only method you need is coalesce (which is identical to the SQL function COALESCE). This is the code:
public static <T> T coalesce(T...ts) {
for(T t: ts)
if(t != null)
return t;
return null;
}
Judging from the example information above, I think this entire article is based on a bugged behavior in Tomcat 6.0.29 and earlier. See https://issues.apache.org/bugzilla/show_bug.cgi?id=50026. Upgrade to Tomcat 6.0.30 and the behavior between (Tomcat|Jetty) should merge.
try this
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.ico</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.htc</url-pattern>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
Edit: This is only valid for the servlet 2.5 spec and up.
I had the same problem and I solved it by using the code of the 'default servlet' from the Tomcat codebase.
https://github.com/apache/tomcat/blob/master/java/org/apache/catalina/servlets/DefaultServlet.java
The DefaultServlet is the servlet that serves the static resources (jpg,html,css,gif etc) in Tomcat.
This servlet is very efficient and has some the properties you defined above.
I think that this source code, is a good way to start and remove the functionality or depedencies you don't need.
References to the org.apache.naming.resources package can be removed or replaced with java.io.File code.
References to the org.apache.catalina.util package are propably only utility methods/classes that can be duplicated in your source code.
References to the org.apache.catalina.Globals class can be inlined or removed.
I found great tutorial on the web about some workaround. It is simple and efficient, I used it in several projects with REST urls styles approach:
http://www.kuligowski.pl/java/rest-style-urls-and-url-mapping-for-static-content-apache-tomcat,5
I did this by extending the tomcat DefaultServlet (src) and overriding the getRelativePath() method.
package com.example;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.servlets.DefaultServlet;
public class StaticServlet extends DefaultServlet
{
protected String pathPrefix = "/static";
public void init(ServletConfig config) throws ServletException
{
super.init(config);
if (config.getInitParameter("pathPrefix") != null)
{
pathPrefix = config.getInitParameter("pathPrefix");
}
}
protected String getRelativePath(HttpServletRequest req)
{
return pathPrefix + super.getRelativePath(req);
}
}
... And here are my servlet mappings
<servlet>
<servlet-name>StaticServlet</servlet-name>
<servlet-class>com.example.StaticServlet</servlet-class>
<init-param>
<param-name>pathPrefix</param-name>
<param-value>/static</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>StaticServlet</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
To serve all requests from a Spring app as well as /favicon.ico and the JSP files from /WEB-INF/jsp/* that Spring's AbstractUrlBasedView will request you can just remap the jsp servlet and default servlet:
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>/WEB-INF/jsp/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/favicon.ico</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
We can't rely on the *.jsp url-pattern on the standard mapping for the jsp servlet because the path pattern '/*' is matched before any extension mapping is checked. Mapping the jsp servlet to a deeper folder means it's matched first. Matching '/favicon.ico' exactly happens before path pattern matching. Deeper path matches will work, or exact matches, but no extension matches can make it past the '/*' path match. Mapping '/' to default servlet doesn't appear to work. You'd think the exact '/' would beat the '/*' path pattern on springapp.
The above filter solution doesn't work for forwarded/included JSP requests from the application. To make it work I had to apply the filter to springapp directly, at which point the url-pattern matching was useless as all requests that go to the application also go to its filters. So I added pattern matching to the filter and then learned about the 'jsp' servlet and saw that it doesn't remove the path prefix like the default servlet does. That solved my problem, which was not exactly the same but common enough.
Checked for Tomcat 8.x: static resources work OK if root servlet map to "".
For servlet 3.x it could be done by #WebServlet("")
Use org.mortbay.jetty.handler.ContextHandler. You don't need additional components like StaticServlet.
At the jetty home,
$ cd contexts
$ cp javadoc.xml static.xml
$ vi static.xml
...
<Configure class="org.mortbay.jetty.handler.ContextHandler">
<Set name="contextPath">/static</Set>
<Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/static/</Set>
<Set name="handler">
<New class="org.mortbay.jetty.handler.ResourceHandler">
<Set name="cacheControl">max-age=3600,public</Set>
</New>
</Set>
</Configure>
Set the value of contextPath with your URL prefix, and set the value of resourceBase as the file path of the static content.
It worked for me.
See StaticFile in JSOS: http://www.servletsuite.com/servlets/staticfile.htm

Categories