I have a web application, which was designed and always worked under root context ("/"). So all css, js and links started with / (for example /css/style.css). Now I need to move this web application to some different context (let's say /app1). It is easy to change server.xml configuration file and bring up web application in new context app1 using [Context path=""] item. But web pages are broken because all links are now incorrect. They point to old root context /css/style.css instead of new app1 context.
Is there any magic way to fix this problem without fixing each link by prefixing with some "context" variable?
Used server - Tomcat 5. Application is written with Java and uses JSP, Struts2, Spring and UrlRewrite filter. More interesting is to hear real experience in fighting with such problems that theoretical debates.
Thank you.
P.S. I do not see how UrlRewrite filter can help me because it will work only in app1 context. So requests to links like /css/style.css will not be passed to it.
If you use URL rewriting to redirect ROOT to your application, won't that eliminate the ability to have a an application in ROOT? If so, what is gained by switching the context?
I think the general way to link resources is to either append a "context" variable and make the link absolute: ${pagecontext.request.contextpath}/css/style.css or just make the link relative: css/style.css
Unless you have specific reasons for being unable to modify the code, I would do a search/replace on the links and be done with it. You should have no more than three or four expressions to find, /css, /images, /javascript, etc.
You should always create urls via url re-writing, not only so that session info can be added to the url, if required, but also so that the context path can be added. You should create all urls as absolutely paths from the top of the application and then let url-rewriting handle adding the context-path to the front, as appropriate.
<c:url value="/css/site.css"/>
That will render /<context-path>/css/site.css or /<context-path>/css/site.css;jsessionid=134345434543 into a jsp file if they don't have cookies enabled. You can also use the c:url tag to render the url into a variable and then use that variable multiple times throughout your document. Just add a var="x" attribute to the tag and then ${x} will render the url into your doc. If you aren't using jsp to render your output, you'll need to find the appropriate mechanism for your view layer, but they will all have one. If you are rendering a url in java code, just take a look at the source code to the c:url tag and you'll see how it is done.
The one awkwardness is that CSS files (and js files) aren't processed, so urls in css and js files need to be relative paths or they will break whenever you change the context path. Most js already uses relative paths since library maintainers don't know what path you are going to install their library to. CSS backround images, on the other hand, are often specified as absolute urls, since the same CSS file may be included into html files at different levels of a file hierarchy. There is no easy fix for this that I am aware of other than to create appropriate symlinks such that the relative url always works or else serve up the problem CSS files via a JSP so that the urls can be rewritten as appropriate. I'm sure there are probably filters or apache modules you can run which will do the url replacement, but then you've still got to manually update your filter/module whenever you deploy to a new context path.
Related
So here is my story.
I am developing a spring web application. The reloading of static content (js, css, jsp) was broken and found a solution in the following thread:
Getting resources in VFrabric Server to deploy without causing container to reload
In order for reloading to work I couldn't have the root URL '/'. So I change that to '/project' and my reloading works. Great!
But then comes the next problem. I have a lot ajax requests to '/typeahead/searchUniversities/%QUERY' for example, this would need to be changed to 'project/typeahead/searchUniversities/%QUERY'. And when I upload it to the production server I would need to change it back to '/typeahead/searchUniversities/%QUERY'
Since '/project' is just for development
So the I read about profiles but I'm not sure if this is the way to go. I might overdoing it?
I was also thinking of having something like '${baseurl}/typeahead/searchUniversities/%QUERY' and then just change in one place before production. But not sure how to do this.
If someone could guide me with an example of how to do this it would be great.
Thank you!
Edit : add another more direct way to get the context path in a JSP
The url should never be static in the webapp : the context path is determined at deployment time and not at compile time. If you are using spring tag library, you can make use of <spring:url> tag, if not of the JSTL <c:url> tag. Both will automatically add the context path for you.
BTW : in a JSP <%= application.getContextPath() %> gives the context path without any additional taglib.
But please only use relative paths where it has sense and never for "top level" URLs.
Your application shouldn't depend on the context path it is deployed under. One solution to this particular issue would be to use relative paths in your ajax requests,
e.g.
'typeahead/searchUniversities/%QUERY' rather than '/typeahead/searchUniversities/%QUERY'
In my application every link generated is surrounded by response.ecodeURL. On some servers encodeURL adds the context-path to the URL.
The application is named "appl". Creating a link /a/b/c.jsp creates on DEV-system the url /a/b/c.jsp, on Prod it creates /appl/a/b/c.jsp
The tomcat is behind an Apache Webserver which connects via ajp, not sure if thats a relevant fact.
EDIT:
The Webserver isn't relevant. I tried going directly on port 8080 and the same happend again.
<c:url> always pre-pends the context path to URLs: this is standard practice and should always be done. If you are using <c:url> then you shouldn't add your own prefix. Also note that <c:url> runs the URL through HttpServletResponse.encodeURL for you. That method adds URL-based session-tracking information (e.g. the ";jsessionid" path parameter) if appropriate.
You should always add the context prefix to all URLs, whether you use something like <c:url> or do it yourself. You just shouldn't do both. When you use the ROOT context, the context path with be "" (i.e. empty string) and nothing will be pre-pended to your URLs.
Would it work to setup some filters using Spring 3 MVC where the paths for javascript files and css files are modified when streamed to the client, by embedding some timestamp in the filename. And then when those resources are later requested another filter then strips those timestamps out?
This would be an attempt to prevent problems of cached js/css files when an application is redeployed
What would I need to do to set this up? How do I setup the filter to replace the paths with a timestamp and then how to I setup the filter to later strp the timestamps out?
I just need info on the Spring 3 MVC configuration for it in the web.xml, I am ok with what the actual code in the filter will need to do
It may be simpler to use Spring's resource mapping <mvc:resources>, that maps a virtual path to the real location of your CSS and Javascript files. The virtual path can contain the version of your application. This means that when you deploy a new version of your application, the path of the CSS and Javascript that gets sent to the browser is different than before and this fools the browser into thinking that they're new resources - and so it reloads them.
For example to map CSS and Javascript files in /resources:
<mvc:resources location="/resources" mapping="/resources-1.2.0/**"/>
This says that any request that comes in with the URL pattern /resources-1.2.0 followed by anything (e.g. /resources-1.2.0/css/styles.css), look for the file in the folder named resources in the web root.
When you update the application version between deployments the virtual path to the CSS and Javascript resources will change and so browsers will be forced to reload the files - even though the real files are in the same old location.
You can make the application version dynamic too - so you don't need to modify your config file.
There's a more in-depth write up of this whole approach here.
My old way using mod_jk in apache and configure virtual host in tomcat
In the JSP file, I refer to CSS as below
/<%=request.getContextPath()%>/css/styles.css
while the home link is set to
/<%=request.getContextPath()%>/
so this worked fine when I use mod_jk in apache to work with tomcat using ajp;
When I try to configure reverse proxy as below
ProxyPass / http://localhost:800/mywebapp
ProxyPassReverse / http://localhost:800/mywebapp
the home page can be retrieved fine but the css request becomes
http://mydomain.com/mywebapp/mywebapp/css/style.css
so the css file can not be retrieved correctly;
I think one possible way is to always use relative path like ./style.css or ../style.css
a. since header/footer are shared, and the home page is in a different level with detail page, it's inconvenient to use relative path because they're at a different level
b. still, I think the home link will have to be /<%=request.getContextPath()%>/
so I wonder what's the way to set contextroot fine in java web and also work fine with reverse proxy?
thx a lot
As I know your application server (Tomcat) isn't able to be aware of a reverse proxy presence. Generally speaking, it can be contacted through any number of reverse proxies or directly by browsers. A network configuration is usually used to limit this, not HTTP or Java.
So, you must accurately rely on relative URLs to make your application work well.
When I have to deal with reverse proxy presence (almost always due to SSO architectures), I embed a "junction" configuration string item (the portion of the URL used in the proxy to map the application) and use it in the only places where I need to build an absolute URL.
I am writing a servlet (specifically with Scalatra). In the servlet I have many links on a table of contents which is included with every page. I want these links to be relative to the application base. If I use links such as "/foo", everything works fine when the servlet is served from the root (localhost:8080/) but if I serve it from Jetty/Tomcat along with other servlets (localhost:8080/servlet) the link points outside of the servlet.
What is a good fix for this problem?
You need to prepend the link URL with a domain-relative path including the context path as obtained by HttpServletRequest#getContextPath(). I don't do Scala, so I can't give a Scala-targeted answer, but here's how you'd do it in JSP so that the picture is sound:
link
When the current context path is /foo, then the above will end up in HTML as
link
Or if you're generating HTML programmatically using Java inside a servlet class (which is actually a poor practice, but that aside):
out.println("link");
An alternative is to set the <base> tag in HTML so that all relative links are relative to it, see also this answer for more detail: Browser can't access/find relative resources like CSS, images and links when calling a Servlet which forwards to a JSP