I have a folder of static html,imgs,flash content that lives outside of the webapp folder. Right now I'm using a symbolic link to map that folder into my webapp directory. The problem i have is when I undeploy my application it follows the symbolic link and deletes all these files.
One of the solutions I'm trying to implement is a special servlet that wraps the default servlet but uses a different relative path. I'm having trouble finding out how to wrap the default servlet in a way that overrides the default servlet path.
Here is what I'm working with:
public void doGet(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
final RequestDispatcher rd = getServletContext().getNamedDispatcher("default");
final HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
#Override
public String getServletPath() {
return "/usr/depot/repository";
}
};
rd.forward(wrapped, resp);
}
You can override DefaultServlet with your own implementation. You can perfectly subclass it, it's a public class. Here are the functional specifications of the DefaultServlet, you need to adhere it.
On the other hand you can ignore DefaultServlet and go for your own solution, an example can be found here.
You can either write your own servlet to serve static content (which is not that hard) or try to extend rather than wrap the DefaultServlet. Either way, your resulting servlet will have be configured in place of default in your web.xml (using "default" as servlet-name).
That said, DefaultServlet will only serve static content from under your webapp context; in order to change that you'll have to create / bind to JNDI your own ProxyDirContext instance pointing to the outside folder and I'm not sure whether that will work; its configuration process is rather involved.
Trying to override servlet path will not get you anywhere.
We have a similar problem that we need to share some files generated by CMS among several applications. Symlink is the easiest way to do this if you are not using Windows.
We setup 2 accounts for CMS and Tomcat. The files are read-only to Tomcat so it can't delete them.
You can also write a small Tomcat extension so it can look for files in multiple places. See this web site,
http://blog.bazoud.com/post/2009/05/12/Multiples-docbases-avec-tomcat
Your current approach won't work. Tomcat needs to load up all the resources in a cache on deploy for it to be available. It's too late to change that in request processing. This extension allows Tomcat load resources from multiple directories. The drawback of this approach is that you have to put a small JAR in server/lib.
That's not a good idea.
Web containers or application servers can be deployed behind Web servers or you can simply use a Web server in conjunction with your container. Just put your static files under that and refer to them by absolute path.
There's really no need for this kind of hack (sorry but that's what it is).
Either that or simply deploy them with the Web app.
You can change to a different path within your webapp context. Here's an example which does differential serving depending on whether the client's User-Agent supports ES6:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher rd = getServletContext().getNamedDispatcher("default");
HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
#Override
public String getServletPath() {
String prefix = supportsES6(req) ? "/es6" : "/es5";
String newPath = prefix + req.getServletPath();
if (newPath.endsWith("/")) newPath += "index.html";
return newPath;
}
};
rd.forward(wrapped, resp);
}
However, "es5" and "es6", even though we use the initial slash, are subdirectories of the webapp's ordinary context. It's not possible to break outside of the context directory using this method.
I have open-sourced a custom servlet that serves files from an arbitrary base path. Additionally, it supports file browsing inside nested compressed archives.
It's available here: https://bitbucket.org/teslamotors/zip-listing/overview
Related
Lets say I have a simple "Hello world" type servlet, configured with the annotation #WebServlet("/hello").
I want to disable it for build/deployment, so it will not be possible to "call" the servlet. How would I do that?
Then, through a configuration file, I want to be able to enable the servlet at run-time, so it can be used by a client. How would I do that?
Is either of these possible?
You can't enable servlets during runtime via standard API. It can at most only be enabled during build time in web.xml or during deploy time by ServletContext#addServlet(). Your best bet is to always enable it and control it on a per-request basis. You can use a servlet filter for this.
First give the servlet a name.
#WebServlet(urlPatterns="/hello", name="yourServlet")
public class YourServlet extends HttpServlet {
// ...
}
So that you can easily map a filter directly to it without worrying about servlet's URL patterns.
#WebFilter(servletNames="yourServlet")
public class YourFilter implements Filter {
// ...
}
In your filter, just decide whether to continue the chain, or to return a 404 based on your configuration file setting.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (isYourConfigurationFileSettingSet()) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
The isYourConfigurationFileSettingSet() part can't be answered in detail based on the information provided so far. In case you actually also couldn't figure out that, then head to Where to place and how to read configuration resource files in servlet based application?
Instead of defining the servlet through an annotation, do it in the web.xml file. Different versions of this file may allow you to have the servlets enabled or not.
The version of web.xml to use should be selected at build and deployment time. Maybe by a Maven profile or similar. Take a look at the following link for some ideas on that: https://maven.apache.org/guides/mini/guide-building-for-different-environments.html
If you want truly run-time control, then you may have to do a little custom coding. A filter (or, I suppose, the servlet itself) could check the value of a property and return a response with an HTTP error code (I suppose 403 would be vaguely appropriate; 404 less so, but if you want it to appear as though the servlet didn't exist in that configuration, it would work...)
In Java servlet, there is <context-param>. In desktop applications, we usually define our own configuration file.
Where should I put configuration parameters for my Struts2 application? For example, my application needs to set a time limit for user input, or save and read files stored somewhere, or the maximum time the user can type a wrong password, etc. I want those things configurable.
What's the way people usually do it in Struts2 applications? Any best practice?
If you are familiar with the ServletContext approach that you mentioned, you can stick with that. In your web.xml, just add your <context-param>s.
Then, to get the ServletContext in your actions, just implement ServletContextAware and it will be automatically injected for you.
Here's a brief example:
web.xml
<context-param>
<param-name>someSetting</param-name>
<param-value>someValue</param-value>
</context-param>
Your Action
public class YourAction extends ActionSupport implements ServletContextAware {
private ServletContext servletContext;
#Override
public String execute() throws Exception {
String someValue = (String) servletContext.getAttribute("someSetting");
return SUCCESS;
}
#Override
public void setServletContext(final ServletContext context) {
this.servletContext = servletContext;
}
}
See here: Apache Struts 2 Documentation - Handling File Uploads
or : Apache Struts 2 Documentation - File Upload
Properties can be set by putting a struts.properties file in WEB-INF/classes. Any property found in the properties file will override the default value.
struts.multipart.parser - This property should be set to a class that extends MultiPartRequest. Currently, the framework ships with the Jakarta FileUpload implementation.
struts.multipart.saveDir - The directory where the uploaded files will be placed. If this property is not set it defaults to javax.servlet.context.tempdir.
struts.multipart.maxSize - The maximum file size in bytes to allow for upload. This helps prevent system abuse by someone uploading lots of large files. The default value is 2 Megabytes and can be set as high as 2 Gigabytes (higher if you want to edit the Pell multipart source but you really need to rethink things if you need to upload files larger then 2 Gigabytes!) If you are uploading more than one file on a form the maxSize applies to the combined total, not the individual file sizes.
If you're happy with the defaults, there is no need to put any of the properties in struts.prop
I typically put all these settings in my struts.properties file located in the default package. They can also be set in the struts.xml file if you use this type of configuration.
A Google search turns up a plethora of file handling examples for struts 2 using "Struts2 file upload" as your search parameters.
I use a config xml document that I load in a class that implements javax.servlet.ServletContextListener class. From there I set attributes to the servletContext:
public void contextInitialized(ServletContextEvent contextEvent) {
try{
Document xmlDocument = readConfigFile(contextEvent.getServletContext().getRealPath("") + fileSeperator + AppConfigConstants.XML_CONFIG_LOCATION);
contextEvent.getServletContext().setAttribute(AppConfigConstants.RECORDS_PAGE_NODE_NAME,this.getValueFromConfig(AppConfigConstants.RECORDS_PAGE_NODE_NAME,xmlDocument));
...
}
Then in my struts base action class I have methods that get the properties from the servlet context.
protected Integer getRecordsPage(){
Integer recordsPage = Integer.valueOf("0");
if(this.getServlet().getServletContext().getAttribute(AppConfigConstants.RECORDS_PAGE_NODE_NAME)!= null){
recordsPage = Integer.valueOf(this.getServlet().getServletContext().getAttribute(AppConfigConstants.RECORDS_PAGE_NODE_NAME).toString());
}
return recordsPage;
}
Let's say I have an application that has to shorten URLs, but also do other things. (like google.com and goo.gl, or facebook.com and fb.me).
It will be easy to simply deploy two applications, but (for now) it's simpler to be just one. Using spring and spring-mvc. I have the following mappings:
#RequestMapping(value="/{shortUrlKey}", headers="Host=foo.br")
...
#RequestMapping(value="/{username}")
Alas, the headers annotation acts not as giving more specific information, but as restricting instead. So if I have these two, only the latter is invoked, even if I open it as http://foo.br/asdf. If leave only the former, it works for those coming from foo.br, and doesn't open anything if the host is different.
So, the questions:
how can I make two handlers for the same paths, but different URLs / Hosts
is it possible to resolve the host dynamically, with a property placeholder configurer (rather than hard-code it in the annotation)
Perhaps both would work if there is some pluggable mechanism for method resolution. Is there such?
My immediate suggestion would be to write a servlet filter (or a Spring HandlerInterceptor), which would take the host name from the request, prepend it to the original requested path, then forward on the request.
For example, given the requested URL http://goo.gl/my/path, the filter would forward to /goo.gl/my/path. The Spring MVC mappings would then have something to get their teeth into. The ant-style wildcard syntax (e.g. "**/my/path") or path-variable style (e.g. "{requestHost}/my/path" might be helpful there.
Alternatively, the filter could set a custom header or request attribute containing the requested host, but that's probably less flexible.
I'm not sure what you mean by the second part of your question, though.
Here's a working snippet:
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (request.getRequestURL().toString().contains(shortenerDomain)
&& !request.getRequestURI().startsWith(SHORT_URI_PREFIX)) {
request.getRequestDispatcher(SHORT_URI_PREFIX + request.getRequestURI())
.forward(request, response);
return false;
} else {
return true;
}
}
Based on your description, it sounds like you could have two controller methods with each domain header mapping:
#RequestMapping(value="/{shortUrlKey}", headers="Host=foo.br")
public void fooBr() { ... }
#RequestMapping(value="/{shortUrlKey}", headers="Host=bar.bz")
public void barBz() { ... }
I am facing a task to add a dependent jar application to an existing one. The existing one does not use to much of locale benefits and so on, but my new one should.
So like I have now: localhost:8080/old-app
I want to have also: localhost:8080/[en|fr|...]/new-module
Could anyone point me the direction, because even if I think I get the idea of filters, filter-mapping, I cannot manage to solve it.
I would like to keep the old one and also have access to the new one.
Deploy new-module as ROOT.war (or set path in /META-INF/context.xml to /). Use Tuckey's URLRewriteFilter to rewrite specific URL's and transform the language part to a request parameter so that it's available by request.getParameter(). It's much similar to Apache HTTPD's mod_rewrite.
An alternative to URLRewriteFilter is to homegrow a custom filter which does like the following in doFilter() method.
String uri = request.getRequestURI();
if (uri.matches("^/\\w{2}(/.*)?$")) {
request.setAttribute("language", uri.substring(1, 3));
request.getRequestDispatcher(uri.substring(3)).forward(request, response);
} else {
chain.doFilter(request, response);
}
Map this on an url-pattern of /*. The language will be available by request.getAttribute("language") on the forwarded resource.
If you dont want the applications name as context root e.g. localhost:8080/appname but under / directly you have to put it into the tomcat/webapps/ROOT folder. To get more sophisticated URL mappings working have a look at http://ocpsoft.com/prettyfaces/
I am using Tomcat. I would like to put the config file in WEB-INF instead of the default root class path which is WEB-INF/classes. Currently I put the config.xml in WEB-INF and use the following relative addressing to locate it:
InputStream input = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("..//config.xml");
Is this the correct way to do?
Or should I use the getServletContext().getRealPath("config.xml") first? But I don't know how to obtain the getServletContext() in a .java. (I tried to new HttpServlet for obtaining getServletContext(), but since it is an abstract class, can't be instanced... how can I get the getServletContext()?)
The method getRealPath() is not guaranteed to work, e.g. if your webapp is not expanded from a war file there is no 'real path' on the filesystem to a file inside the war file.
Since you say you are using a ServletContextListener, you can get the ServletContext out of the ServletContextEvent:
sce.getServletContext().getResourceAsStream("/WEB-INF/config.xml");
You can use getServletConfig() method return an instance of ServletConfig.
ServletContext sc=getServletConfig().getServletContext();
EDIT:
public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException{
ServletContext sc=getServletContext();
...
}