Java Servlet - Mapping a servlet to every URL but a string - java

I have a servlet configured to handle all URLs (*):
<servlet>
<servlet-name>MyServ</servlet-name>
<servlet-class>MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServ</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
I need that for URLS beginning with /static/, it should serve them from the static WEB-INF. That is, MyServ should serve everything but /static.
How can I do that?
UPDATE: To clarify, what I'd like is:
/*/ - Goes to MyServ
/static/dir/file.css - Jetty serves the static file.css from the /dir/.
I'm not sure what web.xml to do, or where to put the static files.
I tried adding this:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
But, when I go to a /static/ URL, I just get:
HTTP ERROR 404
Problem accessing /static/dir/file.css. Reason:
Not Found
Powered by Jetty://
I'm not sure if my web.xml is wrong, or if I'm simply putting the files in the wrong place (I've tried under src/main/webapp and src/main/webapp/lib/META-INF/resources/)
Jetty
I am using Jetty. I want to avoid any other layers, such as Nginx, Apache, etc.
To win the bounty, please make sure you answer works for Jetty.

Your best bet is probably to have a rule for static that occurs before the rule for *.
Rule for URL path mapping:
It is used in the following order. First successful match is used with no further attempts.
The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the servlet.
The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory at a time, using the ’/’ character as a path separator. The longest match determines the servlet selected.
If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles requests for the extension. An extension is defined as the part of the last segment after the last ’.’ character.
If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the resource requested. If a “default” servlet is defined for the application, it will be used.
So it will match the rule for /static/, and stop there.

Your problem can by solved using the Nginx. Nginx serves static content HTML files, images (.jpg, .png, .gif), stylesheets (.css) and JavaScript (.js). These files need not to be processed by the web server. Nginx will do this job.
server {
listen 80;
server_name YOUR_DOMAIN;
root /PATH/TO/YOUR/WEB/APPLICATION;
location / {
index index.jsp;
}
location ~ \.jsp$ {
proxy_pass http://localhost:8080;
}
location ^~/servlets/* {
proxy_pass http://localhost:8080;
}
}

To serve static content you dont even need to have a servlet. You can put your static content in a folder which is directly accessible through your server.
For example if your application name is TestApp than you can place your content in TestApp/static/dir directory. Based on that your directory structure would be :
TestApp
|
|_____ WEB-INF
|
|_____ static
|
|____ dir
By doing above directory structure all your static content e.g. Sample.css will be accessible through below URL:
/TestApp/static/dir/Sample.css
Please look at this question for more information How to serve static content from tomcat
Note that by doing this your static directory will be open to everyone without any restriction which means anyone will be able to access your static content without any authentication. But as its your static content I guess its fine unless you have any reason for it.

First, files that are located in "WEB-INF" directory aren't directly web accessible.
Also, I noticed that your entry "src/main/webapp/lib/META-INF/resources/" does not include a extracted WAR directory, aka web application folder.
Example:
src/main/webapp/[WAR folder]/lib/META-INF/resources/
I assumed that you are using Tomcat. So, after you create your WAR file drop it into "webapp" directory, then start Tomcat. The WAR file should extract into a web application folder of the same name as the WAR file. Now from a browser you should has access to any files outside of "WEB-INF".
example: localhost:8080/[web app folder name]/[some file]
Hope this helps,

Simply put your static content into webapp Directory.
That part can be directly access. using localhost:port/yourAppName/ResourceName

Based on my experience (as already suggested by Srinivasu Talluri), reverse proxy is the answer to your problem.
You could use Nginx See detail configuration or configure Apache to work as reverse proxy.
Detail configuration for serving static content thru Nginx could be find here
When static contents will be handled by the web server itself, then your servlet configuration could be used as is. Thus your servlet will serve only the dynamic resources.
Hope it helps.

Related

Can a tomcat hosted servlet break out of it's base URL?

I've just started writing some java servlets and running them with Tomcat (v7). My understanding of how the directory structure works is that URLs are relative to the name of your application:
So if my app is MyWebApp then by visiting http://server/MyWebApp I'll see, for example, the "index.jsp" file I created in the root directory of my web app.
If said index.jsp has a form with POSTs to "submit" then the broswer will send to http://server/MyWebApp/submit
And in my web.xml file I can point a class which extends HttpServlet to handle this POST request. Something like:
<servlet>
<servlet-name>MyHandler</servlet-name>
<servlet-class>org.me.handler</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyHandler</servlet-name>
<url-pattern>/submit</url-pattern>
</servlet-mapping>
However, suppose my form POSTs to "/submit". Then the browser sends the request to http://server/submit
I don't see any way to intercept this from my web app.
One approach is just to deploy my web app as the ROOT. Is there another approach?
Each web application in servlet container has a context path. It can only receive requests below the context path. For good reasons there is no way to "break out" of its context path (except if the servlet container has a bug).
A servlet mapping is always to be seen as relative to the context path.
You can deploy web applications as root, i.e. with context path "/". See here how to do this for Tomcat.
When your pages contain URLs to the server (href-, src-, action-attributes, etc.) you basically have two options:
Use a relative URL. E.g. the form in your example could simply have action="submit" which is turned into an absolute URL by the browser.
Use absolute URLs. Since (if) you don't want that your application is dependent on a fixed content path, you therefore should (need) to dynamically generate your pages and prefix those URLs with the actual context path.

JSP Tomcat Bad Resources

We are deeper into our project, and have set up a basic web application with eclipse. Whenever we attempt to run the server, we get a 404 error like the following
From my research, I have found I need some sort of web.xml file. Where should put this file what should be in it. How do I make it
Your problem is your trying to access a resource within the WEB-INF folder, that is not allowed and that is the reason you have this error, you must put the jsp under the WebContent folder or any folder under the WebContent folder.
The WEB-INF\classes contains the .class files for your Java classes and it's not a good place to put any resource.
Since the 3.0 specification for servlets the web deployment descriptor (web.xml) is optional.
I hope this could help you to fix your problem
Putting JSPs under WEB-INF makes them inaccessible unless you map them in WEB.XML The purpose of WEB-INF is to hide things because users cannot download or access anything under WEB-INF. You only put JSPs under WEB-INF if you don't want the user to be able to go there by its real name (i.e. http://localhost/app/whatever.jsp) but want to map it to some specific url (i.e. http://localhost/app/somename/)
Mapping a JSP that is under WEB-INF to a URL cane be done with this in the WEB.XML
<servlet>
<servlet-name>somename</servlet-name>
<jsp-file>/WEB-INF/whatever.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>somename</servlet-name>
<url-pattern>/somename/*</url-pattern>
</servlet-mapping>
Of course, if you need to map another URL to the JSP but don't feel the need to disallow the user from going there by its .jsp filename, you can use a URL Rewriting filter for that. In that case, there is no point in putting it under WEB-INF.

How to access my web application with and without / at the end of the URL

In my opinion I have a very simple problem, but I am not able to figure out a working solution.
I have been following web.xml
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/article/*</url-pattern>
</servlet-mapping>
If I access my web application from following url http://localhost/article everthing works fine. If I add a slash at the end, the application run into an ifinitie loop.
I knew this is produced by the getRequestDispatcher("index.jsp").forward(req, resp) call and the web.xml configuration entry /article/*.
If I change index.jsp to /index.jsp, I get forwarded correctly, but all resources are sill mapped to the wrong path. For example, the css files are mapped to article/css instead of css. I think this happens because the getRequestDispatcher, keeps the contextPath.
The index.jsp is placed in the root directory.
How could I access http://localhost/article and http://localhost/article/?
I assume that:
the static resources are relative to the JSP on your file system
the URLs are relative
The browser is going to resolve relative URLs relative to the current URL. You can use the application's context path to provide the correct path:
<link rel="stylesheet"
href="${pageContext.servletContext.contextPath}/css/foo.css">
Now it doesn't matter what the request URL is or what the application context path is; the links will be resolved correctly.
The expression is a bit verbose but you could always shorten it with an application-scoped copy of the string.

Jetty not routing to servlet when web application in sub directory of webapps

I have a web application that is functioning properly (servlets called) when I place the application folder in the {JETTY_HOME}/webapps directory.
If I place a copy of the web application in a sub folder of webapps, then all the static files are called when I browse to the site, but the servlets that I call via ajax are returning 404.
http://localhost/shlaa
calls /shlaa/CommentController.do correctly with no error.
http://localhost/mapapp/shlaa
returns 404 for the ajax call /mapapp/shlaa/CommentController.do
web.xml in both paths /WEB_INF folders contains the following
<servlet>
<servlet-name>CommentController</servlet-name>
<servlet-class>web.CommentController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CommentController</servlet-name>
<url-pattern>/CommentController.do</url-pattern>
</servlet-mapping>
The jetty.xml folder in {JETTY_HOME}/etc contains the following:
<Call class="org.mortbay.jetty.webapp.WebAppContext" name="addWebApplications">
<Arg><Ref id="contexts"/></Arg>
<Arg><SystemProperty name="jetty.home" default="."/>/webapps</Arg>
<Arg><SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Arg>
<Arg type="boolean">True</Arg> <!-- extract -->
<Arg type="boolean">True</Arg> <!-- parent priority class loading -->
</Call>
In order to correctly deploy Java Web application to some Application Server or just simple Web Container (like in your case), the web app (also called web module) MUST have a specific structure. The top-level directory of a web module is the document root of the application.
Here is a Web Module Structure:
Now, what you define in <url-pattern> in web.xml file it is just a logical / virtual path for some servlet. Again, it is NOT a real path to the physical location of the servlet class, but a logical path - you make it up as you wish.
OK, now you must put your webapp with the right structure right in the webapps directory. For example, in the above picture Assembly Root symbolizes your webapp folder. So you take that folder either directly or you can make a WAR (Web application ARchive) and put it right under webapps directory. It's not that in webapps you have some directory and in that direcory there are several webapps. All web apps must be right under webapps directory.
So in your case, if your webapp folder has a name shlaa, you must put that folder right under webapp. Period.
Now quote from the official Java EE docs:
Mapping URLs to Web Components
When it receives a request, the web container must determine which web
component should handle the request. The web container does so by
mapping the URL path contained in the request to a web application and
a web component. A URL path contains the context root and, optionally,
alias:
http://host:port/context-root/alias
Setting the Component Alias
The alias identifies the web component that should handle a request.
The alias path must start with a forward slash (/) and end with a
string or a wildcard expression with an extension (for example,
*.jsp). Since web containers automatically map an alias that ends with *.jsp, you do not have to specify an alias for a JSP page unless you wish to refer to the page by a name other than its file name.
In your case, the URL to your webapp will be
http://localhost:8080/shlaa
Now qoute from the Jetty wiki docs (Jetty/Howto/SetContextPathto):
Using the WebAppProvider
The WebAppProvider's role is to look in the ${jetty.home}/webapps/
directory for any deployable applications (such as *.war), and deploy
them onto a context of the same name as the filename. For example, the
WebAppProvider deploys ${jetty.home}/webapps/MyApp-2.4.war into the
context /MyApp-2.4. There is also the special root.war reserved word
that deploys into the context / . While this is the easiest deployment
mechanism, it sacrifices control over deployment specifics.
To make the long story short just place your web app right under webapps directory to make everything work.
As for the <url-pattern> - feel free to define whatever patter you like.
NOTE: actually the are several ways to configure Jetty, i.e. XML configuration is not the only one. See Configuring Jetty for the details.
Quote from The Java™ Servlet SpecificationVersion 3.0:
Chapter 12 Mapping Requests to Servlets 12.1 Use
of URL Paths Upon receipt of a client request, the Web container
determines the Web application to which to forward it. The Web
application selected must have the longest context path that matches
the start of the request URL. The matched part of the URL is the
context path when mapping to servlets. The Web container next must
locate the servlet to process the request using the path mapping
procedure described below. The path used for mapping to a servlet is
the request URL from the request object minus the context path and the
path parameters. The URL path mapping rules below are used in order.
The first successful match is used with no further matches attempted:
The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the
servlet.
The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory
at a time, using the ’/’ character as a path separator. The longest
match determines the servlet selected.
If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles
requests for the extension. An extension is defined as the part of the
last segment after the last ’.’ character.
If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the
resource requested. If a "default" servlet is defined for the
application, it will be used. Many containers provide an implicit
default servlet for serving content. The container must use
case-sensitive string comparisons for matching.
12.2 Specification of Mappings
In the Web application deployment descriptor, the following syntax is used to define mappings:
A string beginning with a ‘/’ character and ending with a ‘/*’ suffix is used for
path mapping.
A string beginning with a ‘*.’ prefix is used as an extension mapping.
The empty string ("") is a special URL pattern that exactly maps to the
application's context root, i.e., requests of the form http://host:port/<contextroot>/.
In this case the path info is ’/’ and the servlet path and context path is
empty string (““).
A string containing only the ’/’ character indicates the "default" servlet of the
application. In this case the servlet path is the request URI minus the context path
and the path info is null.
All other strings are used for exact matches only.
Example with your names.
Your webapp name is mapapp (it's the application root). You want your CommentController.do to be accessed via
http://localhost:8080/mapapp/shlaa/CommentController.do
Then (if all other requiremants like directory structure etc are met) in your web.xml you put the following:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>Comment Controller</servlet-name>
<servlet-class>com.example.CommentController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Comment Controller</servlet-name>
<url-pattern>/shlaa/CommentController.do</url-pattern>
</servlet-mapping>
</web-app>
The first forward slash (/) in the above url-pattern symbolizes context root / context path (mapapp in this case).
This way it will work.
Here are some usefull links.
The Java EE 5 Tutorial:
Web
Modules
Configuring Web
Applications
The Java EE 6 Tutorial:
Web Modules
Configuring Web
Applications
Jetty
Deploying Web
Applications
Jetty Documentaion Wiki
Hope this will help you.
UPDATE
For the Jetty specific configuration see this link:
How to have a web app respond only on a specific port
Here's what I think is happening.
Just cloning your application shlaa in both the webapps root and the webapps/mapapp will not clone your servlets to a new location.
The static content works because those files are actually present at that location and Jetty will serve them given the right location
For servlets, the server will recognize only one location, the one inside shlaa/web/WEB-INF/classes
(since you cloned your app, I assume your clone will be something like shla/mapapp/web/WEB-INF/classes)
What you have to do is just modify your web.xml so that shlaa/mapapp is also recognized
e.g : (try some combination of below)
<servlet-mapping>
<servlet-name>CommentController</servlet-name>
<url-pattern>/mapapp/CommentController.do</url-pattern>
</servlet-mapping>

Static index.html file - is servlet container being hit first?

In appengine, is index.html served as a static file or there's a servlet container involved first?
For example, lets assume I have the blabla.com host, war/index.html and a user goes to http:/blbla.com/
I don't want a servlet container being hit first in order to determine that it's part of welocme-file-list configured in web.xml and only then appengine will serve it as a static file. Is the only way to avoid server roundtrip is to have a user hit the url http://blabla.com/index.html ?
As per docs https://developers.google.com/appengine/docs/java/gettingstarted/staticfiles say:
By default, App Engine makes all files in the WAR available as static files except JSPs and files in WEB-INF/. Any request for a URL whose path matches a static file serves the file directly to the browser—even if the path also matches a servlet or filter mapping.
So now the question: Which of the following is true If I have specified "index.html" in welcome-file-list and user hits http://blabla.com
appengine knows that it has to servle index.html directly as a static file and my servlet container is not bothered checking welcome-files-list.
appengine doesn't know that it has to serve index.html from static files and my servlet container gets hit just to only check welcome-files-list and then allows appengine to fetch it as a static file.
In case of 2: the only way to have html files served as a static files is having users hit them directly in url, i.e http://blabla.com/index.html?
This is very important moment because it means that your servlet container may be doing additional job of resolving welcome-files-list on every request time which results in wasted cpu which could be avoided should users have specified direct path to the html files.
By default, App Engine makes all files in the WAR available as static files except JSPs and files in WEB-INF/. Any request for a URL whose path matches a static file serves the file directly to the browser—even if the path also matches a servlet or filter mapping. You can configure which files App Engine treats as static files using the appengine-web.xml file.
But you cannot set as static file the url "/".
See the documentation.

Categories