I have the requirement to save some information across restarts and redeployments, i.e. write it to a file when Tomcat is shut down and restore it from the file when it is started. It's similar to the way Tomcat saves session information across restarts (see Persistence Across Restarts).
What's the correct directory for such a file?
What's the API to get the path to this directory?
I'm looking for a solution that works on different operation systems, works across redeployments and does not require any setup or configuration tasks. It should be as simple as Tomcat's session persistence, which just works without any configuration.
Use ServletContextListener - Interface For handling your backup plan.
ServletContextListener - contextDestroyed(..) & contextInitialized(..)
And for Handling Path while store file inside Tomcat-server,
Use this code for retrieving path, request.getRealPath("/").toString()
Above getRealPath("/") will provide you server's log directory path. please change it accordingly whatever nearest you want.
Let me know whether this help in your scenario or not ?
Related
I have a webservice that uses Java, REST, Jersey and runs on Tomcat8. The webservice requires access to a database. Depending on where we are in the process the we may be using a testdatabase, production database or something else. Ideally we would like to be able to set which database to use without requiring a code change and recompile.
The approach we have tried is to have a properties file defining the database parameters and use an environment variable to point to the file. This has proved troublesome, first we've had a hard time defining system properties on the Tomcat server that we can read from the application, also it seems like all the files will have to be defined on the classpath, i.e already configured ahead of time and part of the codebase.
This seems like fairly common scenario, so I'm sure there is a recommended way to handle situations like this?
Zack Macomber has a point here. Don't enable your app/service to look up its settings dynamically.
Make your build process dynamic instead.
Maven, Gradle and friends all provide simple ways to modify output depending on build parameters and or tasks/profiles.
In your code always link to the same file (name). The actual file will then be included based on your task and/or build environment. Test config for tests. Production config for production.
In many cases a complete recompilation is not necessary and will therefore be skipped (this depends on your tool, of course).
No code changes at all. Moreover the code will be dumb as hell as it does not need to know anything about context.
Especially when working on something with multiple people this approach provides the most stable long-term-solution. Customizable for those who need some special, local config and most important transparent for all who don't need or don't want to know about runtime environment requirements!
We have a similar case. We have created a second web service on the same endpoint (/admin) which we call to set a few configuration parameters. We also have a DB for persisting the configuration once set. To make life easier, we also created a simple UI to set these values. The user configures the values in the UI, the UI calls the /admin web service, and the /admin service sets the configuration in memory (as properties) as well as in the DB. The main web service uses the properties as dynamic configuration.
Note: we use JWT based authorization to prevent unauthorized access to /admin. But depending upon your need you can keep it unsecure, use basic HTTP auth or go with something more detailed.
Not sure if in this particular case it is wise, but it is possible indeed to create a .properties file anywhere on the filesystem - and link it into your application by means of a Resources element.
https://tomcat.apache.org/tomcat-8.0-doc/config/resources.html
The Resources element represents all the resources available to the web application. This includes classes, JAR files, HTML, JSPs and any other files that contribute to the web application. Implementations are provided to use directories, JAR files and WARs as the source of these resources and the resources implementation may be extended to provide support for files stored in other forms such as in a database or a versioned repository.
You would need a PreResources element here, linking to a folder, the contents of which will be made available to the application at /WEB-INF/classes.
<Context antiResourceLocking="false" privileged="true" docBase="${catalina.home}/webapps/myapp">
<Resources className="org.apache.catalina.webresources.StandardRoot">
<!-- external res folder (contains settings.properties) -->
<PreResources className="org.apache.catalina.webresources.DirResourceSet"
base="/home/whatever/path/config/"
webAppMount="/WEB-INF/classes" />
</Resources>
</Context>
Your application now 'sees' the files in /home/whatever/path/config/ as if they were located at /WEB-INF/classes.
Typically, the Resources element is put inside a Context element. The Context element must be put in a file located at:
$CATALINA_BASE/conf/[enginename]/[hostname]/ROOT.xml
See https://tomcat.apache.org/tomcat-8.0-doc/config/context.html#Defining_a_context
i'm trying to get the absolute path of my eclipse project :
so i write this;
File file= new File("");
System.out.println(file.getCanonicalPath());
projectPath=file.getCanonicalPath();
so it gives me the right path when i execute it in a class as java application C:\Documents and Settings\Administrateur\Bureau\ready code\JavaServerFacesProject
But when i try to use it in my web application it gives this :
C:\Documents and Settings\Administrateur\Bureau\eclipse\eclipse
So any idea how to do it
Many thanks
Reading a file from a web application is a classical pitfall, whenever you do this you will suffer.
Web apps (especially Java EE) are not thought out to use the file-system for read/write operation, they rely on the fact that the container (in your case Tomcat) knows how to get the needed resources.
So the basic advice is : don't do it.
But since I basically hate this kind of answers I will give you some advice.
Never use the working directory
You never know where your working directory is, and, more often then not, in any production system, the web-app has no right to write on the working directory.
For example, if you deploy your webapp to a tomcat server, run as a service on a windows machine, you'll find that your working directory is \Windows\System32
You really don't want to write some uploaded files there for example...
You have a few option, what I prefer is to set a path into the web-xml and possibly override it from server configuration (using context).
Second option, even better is, to save a path into a db-table accessed by the web app.
ex:
in the web.xml you set
<context-param>
<description>Uploaded files directory</description>
<param-name>file-storage</param-name>
<param-value>c:\app\storage\uploaded</param-value>
</context-param>
Then in the you server.xml (or you could use the context dir and place there a file with the following code) you override this setting in context.
<Context
<Parameter
name="file-storage"
value="E:\app\storage\uploaded"
type="java.lang.String"
override="false" />
</Context>
Check tomcat documentation
Third option, in the slightly happier situation, you want to write just some temporary file, there is the webapp working dir accessible as a servlet context param named :
javax.servlet.context.tempdir
If I where you I would go for the database table.
All this complexity is because you can have multiple instance of the same app on different instances of tomcat, even on different machines, and even different web application into the same instance, so there is no easy way to make a 'relative' path is all situations.
Most web-app resolve to serialize data on db (using lobs or similar objects), whenever there is such necessity, or to rely on some kind of service (FTP, cifs, nfs ...).
In Eclipse, you can set a Working Directory for your project in the Run Configurations dialog.
Have you tried setting a non-default one there?
(If you cannot set anything, you could try the JVM option suggested here: https://stackoverflow.com/a/7838900/1143126 )
Java webapps provide a convenient way to make them run: It’s suficcient to drop the jar file into tomcat‘s webapps folder or upload it using the tomcat manager. If the jar file is named foo123.jar, the web application is soon accessible under http://<host>:8080/foo123/. However, in the majority of cases, there is a problem with the configuration: It’s a good practice to store data in a database, but where can I store the database connection parameters? Usually you have to adapt some server.xml or web.xml or other configuration file to put it there, but this hinders making use of the automatic deployment for such an application.
A “simple to use” web application should request its required configuration on the first run, like a “setup” screen, and then keep it in some place where it survives a restart of the servlet container. Of course, for database connection parameters, storing them in the database is not an option.
Following the specs, the servlet container has to provide a directory that a web application has write access to. It can be determined using:
File tempDir =
(File) session.getServletContext().getAttribute("javax.servlet.context.tempdir");
The content of this directory is bound to the ‘servlet context lifecycle’, if I got it right this means it is empty after a server restart. If that is true, it cannot be used for my purpose.
Does anybody know some kind of best practice for that? I don’t want to reinvent the wheel.
In lack of a better solution, I would implement it this way: As I said, if you make use of the easy deployment means described above, the context path is derived from the jar file name. So I could imagine to make use of this for the database connection as well. In simple terms: If the web application foo123 finds a MySQL connection on localhost:3306 (the MySQL default port) and can connect to it with username foo123 and password foo123 and has permissions to access a schema called foo123 it always uses that on restart.
What do you think of that?
You could just use a context.xml file. That will let you store the config files on a server-by-server basis and that means that you'll never have to put that information in the code itself.
This example seems to sum it up rather nicely.
I have a Struts based web application that has a structure similar to the one shown below. (Not this is just an example)
$TOMCAT_HOME/webapps/myapp
|-css
|-myapp.css
|-js
|-myapp.js
|-forum
|-index.jsp
|-list.jsp
|-users.jsp
|-Articles
|-index.jsp
|-ListArticles.jsp
|-Guestbook
|-viewGuestBook.jsp
|-AddnewEntry.jsp
|-WEB-INF
|-classes
com
|-myapp
|-forum
|-DisplayForum.class
|-ListUsers.class
|-article
|-ArticleList.class
|-AddArticle.class
|-guestbk
|-LoadGuestBook.class
|-ProcessGuestBook.class
Currently, the application is built using ANT and deployed to the Tomcat application server as a single war file (named myapp.war). I would like to separate the application so that it is deployed using multiple war files for each module. (i.e. forum.war, articles.war and guestbook.war). The contents of the new war file will only contain files related to the module. For example, the forum.war file will contain
$TOMCAT_HOME/webapps/forum
|-forum
|-index.jsp
|-list.jsp
|-users.jsp
|-WEB-INF
|-classes
com
|-myapp
|-forum
|-DisplayForum.class
|-ListUsers.class
There are a couple of things i am not sure about with this approach.
Shared static resources
The *.css and *.js files are common across each war file. If i have different war files i will have a copy of the css files in each war file. Is there any way i can deliver the resource files (css, js, static files) in a common approach so that they are shared. I am thinking that maybe i should include a new war file called common.war and include the shared static data. (I think the other war files can access these files using the URL as they are static resources. right?)
Shared classes
There are some classes that are shared globally. As an example, there is a class called UserSession. When the user logs-on to the application, the UserSession object is created and stored in a Hashtable and the user's session id as the key for the Hashtable. Anytime a user tries to acess any part of the application, the session id is checked against the sesion id's in the Hashtable.
The UserSession object does a number of things - such as
Validating user login
Track user's activity
Log user login history to the database
And more...
I need all war files (applications/modules) to have access to the UserSession object but that only one is associated with each session. How should i architect this so that the user's session spans across the different war files?
I have been reading around about how to share objects and came up with two options
Shared object via JNDI
Shared object via Tomcat ServletContext
As i understand it, if an object is stored in the ServletContext, it can be accessed by any application (i.e. war file). How would this work though if i want a different instance per user session. For example,
User1 logs on - UserSession stored in ServletContext
User2 logs on - Where do i store User2's UserSession object?
At the moment i store the sessionID in HTTPSession and the UserSession object in a HashTable.
HttpSession session = request.getSession(true);
UserSession userSession = getUserSession(session.getId());
Does User1 get the same sessionId regardless of the war file he/she is accessing? If so Could i Store another List object in ServletContext that contains the sessionIDs?
I have also seen references to object sharing using JNDI. I am not very familiar with JNDI. I have used JNDI for DataSources but that is as far as it goes. How exactly would it work? I would appreciate if someone can point me to an example showing how JNDI can be used for sharing data. Is it true that JNDI is the better approach and why?
And finally, where would the UserSession.class file reside? I know i can put it in a jar file in $TOMCAT_HOME/lib but this is usually not recommended. The problem though is if it is in the WEB-INF/lib folder of one of the war files it cant be accessed by any other war file.
Would appreciate some input/suggestions. I would be interested to know what strategy you use to deploy an application made up of multiple war files.
Thanks
Edit
Ok i forgot to mention why i want to split the war file. Basically, we have several teams of developers. We have had situations where one team is working on a specific area of the application and another team is working on a different area.
For example assume the following scenario -
Team1 is working on the Forums module due to be released next month.
Team2 has been asked to deliver a change for the Articles and should be released next week.
If Team1 has checked in their code and are performing system/integration tests which will take more than a week, Team2 is stuffed as they have to wait.
These kinds of problems are usually solved using Branching but we tend to avoid branching as it introduces a lot of complications with merging that we decided to avoid Branching.
The other reason is that we have an older application that i am considering to reuse with this application. The older application based on pure HTTPServlet. I.e not based on any framework (i.e. structs, spring etc). If i want to integrate it with my existing app i will need to use the session/servletcontext.
Also, a lot of people are skeptical of the fact that if you make a simple change (e.g. you add a new stylesheet definition to a single css file), you have to rebuild and redeploy the whole application.
Other reasons include
Could Simplify scalability/load balancing - (possibly?) Not sure about this one. I am thinking in terms of deploying each war file to a different server/cluster.
Reduces PERMGEN memory requirements
etc...
I'm not sure if you understood correctly, but the HttpSession is different for each user (browser session), and for each application as well, AFAIK, so you can't use it to share data.
Basically, you need communication between your web applications.
You could have one war to act as data manager for your UserSession, and all other wars communicating to this one. The data manager application needs to expose a service, e.g. UserSessionManager that can be accessed via other applications.
JNDI is a traditional solution for this. Spring has some helpers for this, but the JNDI API is not too complicated either. In the data manager war, you could do something like this in an initialization method:
DataManager mgr = new DataManagerImpl(...);
InitialContext ic = new InitialContext();
ic.rebind("java:global/env/datamanager", mgr);
The interface and the data objects you use needs to be put in a jar that is shared in all 'client' wars, the implementation is only in the data manager war. In your client wars, you can then do:
InitialContext ic = new InitialContext();
DataManager mgr = (DataManager)ic.lookup("java:global/env/datamanager");
Hope this helps,
Geert.
If Team1 has checked in their code and are performing system/integration tests which will take more than a week, Team2 is stuffed as they have to wait.
These kinds of problems are usually solved using Branching but we tend to avoid branching as it introduces a lot of complications with merging that we decided to avoid Branching.
Well, there's your problem right there. This is exactly what branching is for. If it's giving you problems, then the answer is to solve those problems, not to avoid branching entirely. It might be as simple as switching to a better VCS. (Which one are you using right now?)
Also, a lot of people are skeptical of the fact that if you make a simple change (e.g. you add a new stylesheet definition to a single css file), you have to rebuild and redeploy the whole application.
That's the nature of Java web apps. If you put the CSS files into a separate war, then you still have to rebuild and redeploy that war. And with multiple wars, the process will become more complicated, not less.
Anyway, every Java IDE lets you do this with a single key press, so I don't see what the big deal is.
Could Simplify scalability/load balancing - (possibly?)
Possibly. But you know what they say about premature optimization.
I am developing application which is embedded within the cluster environment in Websphere AS. I am using several nodes and sometimes I would like to change configuration settings on the fly and propagate it to all nodes within the cluster. I don't want to hold the config in the db or at least I would like to cache it on the node level and trigger config refresh action which forces each node to refresh the config from some common ground (i.e. db or net drive)
to avoid constant round-trips to the config storage.
More over some configuration can't be stored in db i.e. log level needs to be applied on the logger object in each node separately.
I was thinking about using JMS Topics and publish/subscribe approach to achive that goal.
The idea is that each node could subscribe to each Topic and no matter which nodes initate the config change modification would be propagated to all nodes within the cluster.
Has anyone ever tried to do that in WAS and whether there are any obstacles with this approach. If there are or if you have any other suggestion on how to solve that problem I would be very greatfull for your help.
Tx in advance,
Marcin
Here are a few options to consider as alternatives to JMS -
Use Java EE environment entries. These are scoped to the application, and WAS will automatically propagate any changes to all servers against which the application is deployed. This is a good approach since it is the standard Java EE approach to application configuration, if it is robust enough to meet your use case.
Use a WebSphere Shared Library. This allows you to link your applications to static files external to your application (i.e. on the filesystem), such that they are available on your classpath. Although these files are located on the node file systems, there is a way that you can place these files in WebSphere's centralized configuration repository such that they are automatically propagated to all WAS nodes. For more details on this, see this answer.
Both of these options are optimized for static configuration; in other words, configuration settings that are intended to be set at assembly-time, deployment-time, or to be changed by system administrators, but they are not typically used for values that change frequently, nor are they generally changed programmatically at runtime. WAS does allow your applications to pick these configuration settings in a rolling fashion, such that no application downtime is required though.
Currently we solved the problem with maybe not the most pretty approach but with the most simple one. Since we are using only 2 nodes we have possibility to enter web interface of specific node where we modify settings per each node. Maybe it is not very pretty but for now it is the easiest way. The config is stored in DB and we are planning to trigger config reload in each node and change the log level per node as well.