Serving jetty webapp from two directories simultaneously - java

In development I use jetty as the servlet container. I have the following development configuration:
master project which has wabapp directory
derived project which overrides some of the files in webapp directory
The master project webapp can be started in development mode thanks to providing appropriate WebAppContext to jetty.
Now I want to start derived project analogously, assuming that when request is made, there is an attempt to:
get resource from webapp directory of derived project
if it does not exists, get it from webapp directory of master project
I know that it is possible to override WebAppContext#getResource() method, however some libraries we use in the project seem to perform IO operations on wabapp directory on their own. For example by calling ServletContext#getRealPath("/"), and then reading files without use of ServletContext#getResource() method. The problem could be solved on lower level by some virtual file system on top of File, however it does not seem to be supported in JDK 1.6, any suggestions?

It seems that using something like ResourceCollection is sufficient:
http://download.eclipse.org/jetty/stable-7/apidocs/org/eclipse/jetty/util/resource/ResourceCollection.html
Unfortunately the GWT's DevMode which I use is based on jetty 6, where ResourceCollection is unavailable. I extended the Resource class myself, and together with own GWT JettyLauncher, and thanks to small trick with setting resourceBase on DefaultServlet via reflection, I was able to serve webapp from two directories simultaneously.
protected void doStart() throws Exception {
setClassLoader(new LauncherWebAppClassLoader());
super.doStart();
ServletHolder holder = getServletHandler().getServlet("default");
Servlet servlet = holder.getServlet();
Field field = servlet.getClass().getDeclaredField("_resourceBase");
field.setAccessible(true);
field.set(servlet, combinedResourceBase);
}

Related

How to pass deployment dependant parameters to webapp

In projects I work(ed) on, deployment parameters - such as storage path or DB login - are usually given through a parameter file, which is stored in the war file.
I find that unsuitable because those values needs to be changed each time the webapp is packaged for a different deployment (dev vs prod, change of executing computer). The source code being versioned, this makes it even more bothering.
Is there some better option to pass parameters such as listed above?
By better, I mean:
practical: simple to setup, change and explain to others
separated from the war
as independent as possible to the web container (if dependent, I'm using tomcat in prod)
Edit
I chose the answer of #aksappy to reward the work done in the answer and because it provided several methods using standard tools. However, depending on the context I could go for any other solutions:
method of #Necreaux has best simplicity
method of #Luiggi Mendoza has a good design and is still simple
method of #OldCurmudgeon would be a really good one if the code covered other cases.
You can use a multitude of things based on your environment. Here are somethings which may be considered
Use datasources
The datasources defined in the server context removes the hard wired dependency of managing db configurations and connection pool from the web application. In Tomcat, this can be done as below in the context.xml
<Context>
...
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource"
description="Employees Database for HR Applications"/>
</Context>
Use Contexts
You can configure named values that will be made visible to the web application as environment entry resources, by nesting entries inside this element. For example, you can create an environment entry like this: (Source here). This can be set as context parameters or environment entries. They are equivalent to the entries made in a web.xml or a properties file except that they are available from the server's context.
Use database configurations and load those configuration at ServletContextListener
Another approach which I tend to follow is to create a relational schema of properties in a database. Instead of loading the properties file during server startup, load the properties from the database during start up.
public class ContextInitialize implements ServletContextListener {
private static Properties props;
public void contextInitialized(ServletContextEvent servletContextEvent) {
// connect to DB
// Load all the key values pairs as required
//put this into a Properties object, or create a hashtable, hashmap ..
}
//Getter
public String getProperty(String key){
// get value of key
}
//Setter
public void setProperty(String key, String value){
// set value to a key
}
}
Note: above is just an example.
Use environment variables or classpath variables
Use classpath / path variables in Environment variables and use System.getenv() in your java code to get these values as necessary.
We normally put our web app properties files in the Tomcat home folder. POJOS look on the launch folder. There will be other standard locations for other web servers.
final String tomcatHome = System.getProperty("catalina.home");
if (tomcatHome == null) {
// POJOs look in "."
searchPaths.add(".");
} else {
searchPaths.add(tomcatHome);
webApp = true;
}
An strategy is to pack all the properties and configuration files in an external jar and make this jar a dependency for your application(s): war, ear, etc. Then, you can deploy this jar in a common folder where the application server will load it and make it available for all the applications deployed there. This means that you will deploy the jar with the values for each environment once (or every time you need to change it, but its changes must be slow compared to the changes made to your main artifacts) and you can deploy and redeploy your war or any other project in your application server without problems.
In case of Tomcat, you may deploy this jar inside %CATALINA_HOME%/lib as explained in Tomcat Tutorial. Class Loader Definitions
To consume (read) these files in my application, I just load them like any other resource in my application.
Two strategies I've used:
JVM Parameters -- Custom JVM parameters can be set by the container at startup. This can get a bit verbose though if you have a lot of settings.
Configuration Files -- These can be read by the application. Either the location is hardcoded, put inside the container path, or to get the best of both worlds, specify the location via a JVM parameter.

Can I have multiple Java webapps with overlapping paths?

I have a large collection of different independent (stateless) web services written in Java and compiled as WAR files. I want to deploy them to a single web application server.
If the URIs handled by the services in each WAR file began with a prefix I could use as a web app name, then this would be easy. I could, for instance, have
SALES WAR FILE: contains code for the following:
GET http://example.com/sales/widgets
POST http://example.com/sales/widgets
GET http://example.com/sales/sky-hooks
MARKETING WAR FILE: contains code for the following:
GET http://example.com/marketing/widgets
PUT http://example.com/marketing/sky-hooks
...in which case I would simply deploy two WAR files under the names "sales" and "marketing". However, I am not that fortunate. Instead, the URI paths handled by the components overlap. Something like this:
SALES WAR FILE: contains code for the following:
GET http://example.com/widgets/sales
POST http://example.com/widgets/sales
GET http://example.com/sky-hooks/sales
MARKETING WAR FILE: contains code for the following:
GET http://example.com/widgets/marketing
PUT http://example.com/sky-hooks/marketing
My question is how (if at all) I can deploy these on a single web application server.
I am open to suggestions that require a significant amount of work. For instance, my best-so-far idea is to build services that expect a component-name prefix before the regular URI path, then pipe all incoming traffic through a different server that knows what component each URI pattern falls into and modifies the URI to add that prefix. The difficulty with this approach is that tools like Swagger that read my source code will have a mistaken idea of what the URIs look like.
Any ideas?
If you're willing to put apache in front of your web container, you can use apache's mod_proxy to forward request to the right place.
One way this could work, would be deploy the separate wars at separate prefixes as in your first case (sales and marketing) and then use ProxyPass to send the requests to the correct place:
ProxyPass /widget/sales http://example.com/sales/widget
ProxyPass /sky-hooks/sales http://example.com/sales/sky-hooks
ProxyPass /widget/marketing http://example.com/marketing/widget
ProxyPass /sky-hooks/marketing http://example.com/marketing/sky-hooks
Its probably a better idea to just refactor your routing though - it might be hard to maintain.
(EDIT: I originally suggested mod_rewrite, but I wanted to make my answer more specific, and it looks like this could be done purely with proxying)
If I understand your question correctly, one of the solutions would be (I am assuming Tomcat is used but this should apply to most of the modern servlet containers):
1) Deploy your sales and marketing wars with different prefixes. I.e., using your example, they should be able to serve the following urls:
GET http://example.com/sales/widgets/sales
POST http://example.com/sales/widgets/sales
GET http://example.com/sales/sky-hooks/sales
GET http://example.com/marketing/widgets/marketing
PUT http://example.com/marketing/sky-hooks/marketing
2) Use UrlRewriteFilter to craft lightweight web application that will be deployed to your servlet container root prefix (for Tomcat it is called ROOT.war) and will rewrite urls in incoming requests to point to relevant web application.
In other words, incoming request like:
/widgets/sales
will be transformed to:
/sales/widgets/sales
... and delivered to sales webapp.
Similarly, in response urls like:
/sales/widgets/sales
will be rewritten to:
/widgets/sales
3) Deploy this war to root of your servlet container.
This approach is somewhat similar to the one suggested by #nont but does not require apache as a frontend as the rewriting functionality will be handled by root web application (UrlRewriteFilter basically implements mod_rewrite functionality).
In other words you'll be able to deploy all your applications (including this rewrite application that is deployed to the root prefix) to single server alleviating need for extra intermediate proxy/rewrite servers.
First, Determine How the Deployments may be Configured
Are you sure the absolute URIs must overlap? The context root will prefix the path supported by each service, unless the absolute path has somehow been coded into the application itself. The first step is to enable direct access to each WAR file, either through unique context roots or application instances.
Option 1: Set the Context Root for each WAR File Explicitly
The context root for a war file is set at deploy time. For some servers, this can be set outside of the web application using an external deployment descriptor. For Tomcat, it may embedded within META-INF/context.xml. See http://tomcat.apache.org/tomcat-7.0-doc/config/context.html for more information.
Option 2: Separate the Context Root Instances using Multiple Containers
Alternatively, deploy each war file to a separate instance of a Java EE servlet container, each running on a different port. This will solve the deployment conflict in the case of a hard-coded absolute path.
Finally, Set up a Virtual Host and Proxy the Requests via Apache and mod_jk
Once the context roots instances have been made uniquely accessible by one of the previous methods, configure an instance of Apache to serve as a reverse-proxy. First, set up a virtual host to handle requests for the externally visible URI. Next, configure mod_jk to route the requests to the correct WAR file deployment. See http://tomcat.apache.org/connectors-doc/webserver_howto/apache.html for more details.
Afterthoughts
The above solution approach is generic for this type of problem and will require some knowledge of Apache and Tomcat configuration, which were chosen as example reverse-proxy and Java EE servlet technologies for its implementation. Additional detail on the deployment constraints would help to determine an optimal solution. In general, identifying the hard constraints on what may be changed versus what may not be changed should guide you quickly to a solution.
The obvious slotuion is to rename the wars, or refactor so that the appropriate mappings are in the correct place.
Anything else is going to be a bit hacky, you can't change the war name, even to soemthing like below :
SALES WAR FILE: contains code for the following:
GET http://example.com/webapp1/widgets/sales
POST http://example.com/webapp1/widgets/sales
GET http://example.com/webapp1/sky-hooks/sales
MARKETING WAR FILE: contains code for the following:
GET http://example.com/webapp2/widgets/marketing
PUT http://example.com/webapp2/sky-hooks/marketing
You could also create another war for routing/filtering, that redirects everything appropriately - but that also relies on altering url somewhat.
This is a use case for Reverse Proxy. If your web server is Apache, as suggested by #nont proxy_mod can be used to create a reverse proxy.
I know that IBM Http Server (IHS) also allows this mod.

Load webapp on container startup without web.xml in Tomcat+Jersey?

I currently have a Jersey webapp without a web.xml. It deploys nicely, but doesn't start up until it receives its first web request.
To get the webapp to load at startup, I could create a web.xml for the webapp and give a load-on-startup tag. However, I'd strongly prefer not to make a web.xml.
Is there a way to get a JAX-RS application to load at startup without web.xml? I'll even accept a solution that is specific to Jersey and/or Tomcat.
EDIT: I would also accept a solution that loads all apps in a Tomcat instance eagerly.
EDIT: Let me give a little more information on how the app is being deployed, per a comment.
The deployment process is not sophisticated.
The App will live on an EC2 instance running Ubuntu 12.04. I'm setting up one instance of the App by hand; once it works, I will make an AMI of the app and create additional copies of it as needed.
To deploy the app on the initial instance, I'm simply copying the WAR file to /var/lib/tomcat7/webapps and restarting Tomcat. No other webapps will be running on this Tomcat instance.
If any additional information would be useful, let me know! I'll happily add it.
EDIT: For clarity's sake, this is how my webapp Application class looks, at a high level:
#ApplicationPath("/")
public class App extends ResourceConfig {
// ...
}
I'm using the Jersey-specific ResourceConfig class instead of the more general JAX-RS Application class because I'm using Jersey's built-in HK-2 to do some dependency injection.
The only way I can think of to do that is to switch to setting up the Jersey ServletContainer yourself and set its "load on startup" value to something greater than zero. You might use a ServletContainerInitializer (no relation--the naming is just a coincidence) to do it. If you happen to be using Spring, its WebApplicationInitializer offers the same mechanism with a slightly more convenient interface.
Another, rather hacky, way would be to write a class that extends ServletContainer and give it an appropriate Servlet 3.0 annotation, something like #WebServlet(value="/", loadOnStartup=1)
One solution would be to force a first request to the app by simply adding a call to curl or wget to your deployment script. It has the additional advantage of warming up any caches. And it can be used for testing if the deployment and the app really work. (Just check HTTP status code or some text on the response page...)

Get the CATALINA_BASE property from inside a Java Web Service

Dear fellow Developers,
I am working on improving my Java Web Services, and I am trying to use a more delicate way of getting the directory path of properties files in a Java Web Service.
In order to make my Java Web Application easier to be deployed on an Apache Tomcat Server, I add the following line to the web.xml file:
<env-entry>
<env-entry-name>loggerPropertyFile</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>/Some/Long/Directory/File/Path/Which/May/Change/conf/LoggerInfo.properties</env-entry-value>
</env-entry>
As the above xml code depicts, I have placed a Properties file somewhere in the local Filesystem, and I want my Web Service to initialize its logger class, based on that configuration. As you can realize this path changes every time I deploy my web service to another server.
Thus, I figured out that I may be able to use the $CATALINA_BASE property, in order to make the environment entry path smaller. How can I retrieve the CATALINA_BASE value from inside my Java Web Service's Code (how is done on Linux and how is done on Windows)??
Thank you.
try System.getProperty("catalina.base");

An address of a website deployed using Tomcat and Eclipse

I'm trying to run a website using Tomcat and Eclipse. I created a Dynamic Web Project, I configured web.xml file and I also used Maven. In a directory src/main/webapp I put an index.html file. I also made a simple REST service in the same project. So this REST service is working for me (for example, when I put "http://localhost:8080/RESTfulService/rest/item" in an address bar. But what is the address that I should write to get an access to a website I put in a webapp folder? I thought "http://localhost:8080/RESTfulService/" should be working, but it's not.
From what i understand of your setup, try "http://localhost:8080/index.html". Do you have a context path called 'RESTfulService' setup?
Do you have a context element listed in your server.xml? If so, what does it say?

Categories

Resources