My Java web application (myapp.war) ist deployed by placing it into the webapps direcotry on Tomcat on Ubuntu 10.04.
This application needs to save some data in files. But the user, which is running Tomcat (tomcat6) has no write access to the home directory /usr/share/tomcat6/ and no write access to the current working direcotry /var/lib/tomcat6/, since both belong to root.
So where should a web application store its data? I hope it is not the extracted archive in the webapps direcotry. This one could be deleted very easily by accident. And Tomcat can be configured, not to extract .war files. Then there would be no extracted direcotry.
Perhaps /var/lib/tomcat6/ should belong to user tomcat6 ant this is a bug in Ubuntu?
If the files need not persist longer than the life-cycle of the servlet context, the servlet container provides a private temporary directory for each servlet context, specified by javax.servlet.context.tempdir attribute.
See Servlet Specification 2.3, Chapter 3 Servlet Context
3.7.1 Temporary Working
Directories
The convenience of a temporary
storage directory is required for each
servlet context. Servlet containers
must provide a private temporary
directory per servlet context and
make it available via the
javax.servlet.context.tempdircontext
attribute. The object associated with
the attribute must be of
type java.io.File
Answering his own question, Witek stated /var/lib/tomcat6/webapps/ is writable -- at least on his installation of his version of Ubuntu. On my RHEL 5.2 system /var/lib/tomcat<X> doesn't even exist, so there is no webapps subdirectory writable or not, which leads to my answer.
Q: Where should a Java web application store its data?
A: Wherever you've configured it to store its data.
Make the location configurable, in web.xml as a <context-param> or in a myApplication.properties file.
I can put it where I want on my box, the SysAdmins can put it where they want on the production system.
You can change your mind later.
You don't need symbolic links (which have magically disappeared on me before, breaking the system.)
You can have several sets of test data, and just point the configuration at whichever one you want.
You can put it wherever there's disk space.
You are going to change your mind later.
I think it depends on what kind of data you are dealing with. Most of the time, data goes into the database simply because it is fast and easy to perform a CRUD. If you want to store localized user configuration and you don't care how portable it is, perhaps you can store under user.home, I did that for one of my projects and that works fine. All that being said, I really don't think there's any best practice on this and database seems to be the most obvious choice because you can do whole lot of different tasks against it, and most of them are free to begin with. :)
I found the solution on Launchpad. /var/lib/tomcat6/webapps/ is writable. This means, that the following works:
File myFile = new File("webapps/myfile.txt");
I haven't seen any specific guidance on where you should store that kind of data locally - probably because you'd normally store that kind of data in a database.
When I need to store quick-and-dirty data like that, I store it on a filesystem specifically for that data, to isolate it from other areas where it might get caught up in other activity. Haven't had any issues with the approach so far. Also, if it's important, make sure you store it somewhere where you're backing it up :)
Related
I have a program which permits to upload a file from somewhere into another place. This another place is the path that will be on database. Where should I upload this file:
Into c://?
Into projectSomething/ (I´m working with glassfish and in this folder it saves on C:\Users\Alvaro\Documents\glassfish-4.0\glassfish\domains\domain1\config)
Into projectSomething/src/
Answers to such questions are always (at least partially) opinion based which is bad on SO. But I'll try to give you what is considered as good practice.
As you speak of glassfish, I suppose it is a web application. For evident security reasons, you should never allow uploaded files where you could have any of your application files. You should also think that some servlet containers or Java EE servlet can display an application without exploding the war. It is clear that in such cases, you won't be able to write anything under your project root!
So where can you put it? IMHO, the best is to see that as a deployment detail and configure it in a property file, a java system property or in a environment variable and clearly identify that in your documentation. That way it is no longer your problem as a programmer. You will be able to deploy it under windows or linux (where C:/ does not exists) under a special directory under your project root if it is guaranteed that the war will be exploded and the application will have appropriate permissions on that directory.
Our system is split up into different environments, each one as a separate Tomcat instance.
Development (Windows)
QA: Accessed by our QA department (Linux)
Production: Live site, accessible to customers. (Linux)
Each of these environments rely on separate databases, and several other web services. This means we need to keep track of the various URLs, usernames, and passwords that are all different. Many of these settings are shared across several apps, so it would be ideal to have them all in one place to remove duplication.
Right now we have config files in the application itself. We use Maven profiles to fill in the different config settings when we build the app. But this is clumsy because we have to build a different WAR for each environment.
Where is a good place to store the config files so that we can deploy the same WAR file to each server?
I've done a fair amount of research on this already. But I haven't found an solution that completely makes sense to me yet.
Separate Config Directory
Define a directory to hold config files. Such as /opt/config on linux.
I like this idea, but how do I tell Tomcat where this directory is? I see references to context.xml, but every example I've seen puts the context.xml in the META-INF folder inside the WAR. Is there a way to configure this outside the WAR?
System Property to define environment
This involves setting a system property, and then using some sort of if/else or switching logic to load the appropriate config file. This seems workable, but a bit messy. But where/how do you set this property? I typically start tomcat with ./startup.sh. Do I add arguments to that command or is there another configuration somewhere?
JNDI
I don't think this is an option for us. Every tutorial I've looked at for this seems to be dependent on LDAP or something similar. To my knowledge we don't have that available to us, and it seems like too much overhead to get set up for only a half-dozen config files.
Use system property that refers to the location where your configuration file or directory is located. In this case you can manage different environment easily and no if/else logic is needed.
You application can have hard coded value of config file path, that will allow running application without any additional system property. The application however should fail to start if mandatory data is not found.
Concerning to partial sharing of data among environments.
You can split your data into several files by categories. Some files will be shared, some other different for different environments. You can even develop your own mechanism of references between data files.
However better approach is using some ready-to-use packages. For example Spring framework supports very flexible configuration mechanism. However if you already have Spring-less application introducing this framework for configuration only seems like an overkill. In this case take a look on Apache Commons Configuration package.
Worked with my team on this and we came up with what we feel is a cleaner approach. While every tutorial I found put the context.xml inside the WAR, it can also be placed in the conf folder of the Tomcat directory.
This works for us as all our servers are Tomcat based. So each server can have it's own context.xml which has a property pointing to the config folder on that particular server.
A lot of the advice on the web on storing variables which may change depending on the env/other conditions is to put them in web.xml, but isn't the web.xml within the war file? even if you find the exploded war and change it, wouldn't it get overriden if you update the war file? Or does the webcontainer provide any method to configure the web.xml without tinkering with the war file?
The web.xml variables are of very limited use, in my experience - the only advantage is that it's a standard location to look for hard-coded "configuration".
There are several common solutions to get a more sensible way to configure web apps, none of which is standard:
Use system properties (which usually involves fiddling around with startup scripts, and it can be hard to get a good overview of your entire config)
Use environment variables (same drawbacks as system properties)
Read a config file from a predefined location; often from the classpath by using getResourceAsStream (IIRC that usually means putting the config files in Tomcat's lib directory)
You can also use JNDI, which has the disadvantage of being rather heavy-weight both to set up and read (if you're using vanilla Java, anyways - Spring for example has rather good support for reading from JNDI). However, JNDI is rather good because it's per-application, and not a process-global setting. If you need to run several instances of the same app on the same server, JNDI is pretty much the only option (although you can use it to just point out a config file somewhere, which makes things easier to work with).
This may be relevant to your interests: How can I store Java EE configuration parameters outside of an EAR or WAR?
Advantages of specifying Parameter Values in web.xml
Using your own settings file requires additional coding and management.
Hard-coding parameter values directly into your application code makes them more difficult to change in the future, and more difficult to use different settings for different deployments (eg: JDBC settings, mail server address).
Other developers using your code will be able to find any relevant parameters more easily, as this is a standard location for such parameters to be set.
See also:
Advantages of specifying Parameter Values in web.xml
Web.xml.EnvEntry
Referencing Environment Variables in web.xml
As far as I know web.xml does not provide ability to store custom variables. Typical way to configure your web application is to store configuration in database, separate properties/xml/json/other file, get configuration from separate web service or provide it through environment variables.
Often a mixture of all these is used. For example you can add system variable using -D switch when running your container. This variable will contain path to file or URL where your configuration can be found.
You can supply parameters using OS environment.
You choice should depend on how many parameters do you have, what kind of application are you developing and how can you configure application server or computer OS. For example if you a hosting application on server you cannot configure these ways are not for you, so DB or web service are your only ways.
The folks that work on the Tomcat container recognize the irony that you have identified and have implemented a way to work-around the issue.
The solution that they implemented for the issues that you have alluded to is to create another xml file... the context.xml file, which is read by the server.
It appears that you can edit this file and have the new values read by the Tomcat without a restart... as long as you keep the elements out of the server.xml.
I do not use Tomcat so I might be mis-interpreting the docs
The GlassFish web container supports a similar feature, but does it via a couple admin cli command (asadmin):
set-web-env-entry
set-web-context-param
There is probably web admin console support and you can set them up by editing the domain.xml. It seems like it isn't as flexible as the Tomcat implementation... but it does make it really easy to use.
You need to disable and then enable your application for the changed values to 'take'. Do not redeploy you app, since that will delete the value that you just set.
I have a single .war file with my web-application. The root of the problem is that I want to keep one .war file for all customers (which have different environment). For this I should keep different versions of libraries in .war and decide which of them to use while deployment.
For example:
First customer have oracle 10, the second one have oracle 11. I want keep both ojdbc.jar files in my war, and choose which of them to use according to some parameter in properties file.
One solution to solve this problem is writing my own ClassLoader, which will not load useless files(as shown here: https://serverfault.com/questions/317901/tomcat-possible-to-exclude-jars-during-app-deployment).
The problem is that our ClassLoader class should be into "tomcat\lib" directory. This do installation of my application is more complex.
Maybe there are another ways how to solve this problem? It will be great to do it programmatically inside my application, using reflection or something else.
Thanks for help!
IMHO the best practice for the example you give (database driver) is to provide a datasource through tomcat. This implies that the database driver jar needs to be in the global classpath (tomcat/lib). This would also keep your intent to have the same .war file for all customers - when you update this, you only need to provide an identical file for all users.
In the case of a database connection, you'll most likely need a customized database for each customer anyway - providing this as a datasource through tomcat makes it necessary to configure it once (on first installation), but you won't need to worry about it afterwards.
If database drivers are not your only problem, please update your question with more examples of differing environments/jars.
Within a Java EE environment (happens to be WAS 6.1 but could be any application server) I need to place a XML file, which is a configuration file, so that I can read and write to it.
This needs to be available in a clustered environment so I am looking at using the class path to load the file.
I am thinking I can store this file in the EAR root, reference it in the manifest and then load and save it.
I have tried this approach by having my file in a JAR and making this available via the MANIFES and I can load the config file from the class path no problem using the following.
this.getClass().getClassLoader().getResourceAsStream("configFileName");
That loads the file that is in the JAR, which is fantastic. But if I want to edit this file, programmatically, I cannot access the JAR files location (the EAR root) it returns me an interpreted path like this:
/usr/IBM/WebSphere/AppServer/profiles/AppSrv01/installedApps/localhostNode01Cell/MyApp.ear/MyApp.war/TB_config.jar
That is not the correct location of the JAR the correct location is at MyApp.ear.
So the question is: how can I access and update (copy contents, create new, save, delete old) the JAR with my config file.
Or should I put the config file somewhere else?
What is the standard Java EE to make files that need read/write access available to WARs on a cluster?
Ok I have built a solution for this. It is more WebSphere based (our platform) but it is J2EE and I am suprised it was not mentioned. Basically I have used JMX to synchronise the nodes. The files are stored, and saved to, the deployment manager the nodes are then resynchronised using JMX calls and then the engines withing the applicaitons are restarted by calling servlets within the applications.
It works a dream
So #stacker, nodes are managed and the manager distributes files to the nodes.
The problem that you've hit is not unique. A lot of Java EE programmers can struggle with providing a "configurable" property file to administrators of a cluster. And the solution that you've chosen, well, has its limitations.
The problem with embedding a config file inside a JAR, is absolute path or the physical path of the file, in case you need to update it. If your container will not explode your EAR and WAR files, then placing the config file alongside the code is a bad idea - the administrator will have to deploy a newer version of the EAR/WAR/JAR. That is unless, of course, you can configure the container to explode the artifacts - WebLogic Server does this, I'm not sure about WAS.
There are several ways to resolve this problem:
Store the config file in a SAN that is accessible to all the nodes in the cluster via a 'canonical' path. That way, you could locate the file from any node in the cluster and update it. Remind yourself to restrict access to this directory. Although this sounds simple, it need not be - Java objects might have to be 'flushed' across nodes, once the configuration file has been updated. Moreover, you might have to cater to the scenario where property files can get edited outside the application.
Use a database. Much simpler and almost hasslefree, except that the Java objects might have to be flushed again.
Use a MBean. As good as a database, except that I haven't known a lot of people vouching for the MBean support in WAS. Also, I'm not really sure if object states can go haywire across a cluster, in this case.
You cannot write to an ear file, you should place the XML file in the DB as a text lob (large object).
Actually, as I am using WebSphere, it appears I can use the dynamic cache provided by the WebSphere deployment manager. The last chapter in the link below dicusses the use of the Dynamic Cache providing a shared object in a cluster. The configuration file is XML that is parsed as such by the engine (into a Document object) of the application and so is a Java object, thus it can be placed into the DistributedMap.
Looks like a clean solution. Thanks all for reading and your replies.
http://www.ibm.com/developerworks/websphere/library/techarticles/0606_zhou/0606_zhou.html