where/how to setup configuration resources for Tomcat .war files - java

I have a source tree for a .war file that I need to modify so that I can add some application-specific configuration info (in this case a jdbc connection string, but I may have other properties-like resources). What are the best practices for where to put configuration info and how to access this from within the Servlet?
I'm guessing this Tomcat configuration reference has something to do with it, but my eyes glaze over when I try to read it.

For web app configuration you can place the config on the classpath somewhere. Then you can get to it from your application with getResourceAsStream or if you prefer Spring:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:my-config.properties</value>
</list>
</property>
</bean>
There are a number of places you can put the properties on the classpath in Tomcat. in order it looks at:
/WEB-INF/classes of your web application
/WEB-INF/lib/*. jar of your web application
$CATALINA_HOME/common/classes
$CATALINA_HOME/common/endorsed/*.jar
$CATALINA_HOME/common/i18n/*.jar
$CATALINA_HOME/common/lib / *. jar
$CATALINA_BASE/shared/classes
$CATALINA_BASE/shared/lib/*.jar
For example, if you put my-config.properties both in a .jar file and in WEB-INF/classes the one in WEB-INF/classes will be used. You could use this mechanism to default to test config and override prod config on prod servers.

For the specific case of a JDBC connection string, I would recommend using a Tomcat-managed connection pool instead. You can read more about doing this here:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html
It's more work, but I think in the long run it will serve you better.

Hmm. It looks like the easiest path to getting what I want on the Java side of the application is to use Servlet.getServletConfig().getInitParameter(parameterName) e.g. getInitParameter("myApp.connectionString");
But I don't know where to set this. The Tomcat docs talk about various permutations of context.xml but I want to make sure this parameter only affects my servlet and not any others. I also don't want to locate it within my .war file so that I can keep this parameter independent of the applications (for instance if I install an upgrade).
Update: I figured it out, key/value parameters accessible by ServletContext.getInitParameter() go here (or can go here) in ${CATALINA_HOME}/conf/server.xml:
<Server port=... >
...
<Service name="Catalina" ...>
<Engine name="Catalina" ...>
...
<Host name="localhost" ...>
<Context path="/myWarFile">
<Parameter name="foo" value="123" />
<Parameter name="bar" value="456" />
...
</Context>
</Host>
</Engine>
</Service>
</Server>
This sets two parameters, "foo" = "123", "bar" = "456" for the servlet myWarFile.war (or more accurately with the URL path /myWarFile) and I can get at them in Java with Servlet.getServletConfig().getInitParameter("foo") or Servlet.getServletConfig().getInitParameter("bar").
I also looked at JIRA's server.xml entry (and what they tell you to set it to for MySQL), they use a Resource rather than a Parameter, not quite sure of the subtleties of this but it seems like it could be more appropriate method.
<Server port=... >
<Service name="Catalina" ...>
<Engine name="Catalina" ...>
<Host name="localhost" ...>
<Context path="/jira" docBase="${catalina.home}/atlassian-jira"
reloadable="false">
<Resource name="jdbc/JiraDS" auth="Container" type="javax.sql.DataSource"
username="jirauser"
password="..."
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/jiradb1?autoReconnect=true&useUnicode=true&characterEncoding=UTF8"
maxActive="20"
validationQuery="select 1"
/>
</Context>
</Host>
</Engine>
</Service>
</Server>

you can add the path to your properties files in your CATALINA_HOME/conf/catalina.properties in the "common" classloader common.loader.

Related

How to define default context elements with Tomcat?

We deploy an application on tomcat 9 (apache-tomcat-9.0.22).
The official documentation (https://tomcat.apache.org/tomcat-9.0-doc/config/context.html) says it's possible to define default context elements but it's not working for us.
We need to define a datasource and a mail server. If we define this resources in conf/server.xml file in GlobalNamingResources it works.
<GlobalNamingResources>
<Resource name="mail" type="javax.mail.Session"... />
<Resource name="jdbc/mydb" type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"... />
</GlobalNamingResources>
But in production, we cannot modify server.xml file. So we need to define this resources in an other file.
If we define resources in $CATALINA_BASE/conf/[enginename]/[hostname]/ROOT.xml file with a war named ROOT.war, it works :
<?xml version="1.0" encoding="UTF-8"?>
<!-- Context configuration file for my web application -->
<Context>
<Resource name="mail" type="javax.mail.Session"... />
<Resource name="jdbc/mydb" type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"... />
</Context>
This solution could be satisfactory but our war file must have a different name than ROOT.war (like MyApp_v42.war) and it will change with every update. We cannot rename the xml file every time we update.
If we define resources in the $CATALINA_BASE/conf/context.xml file or in $CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default file like documentation says we obtain a javax.naming.NameNotFoundException.
Thanks in advance!
One solution is :
Define resources in conf/context.xml :
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="mail" type="javax.mail.Session"... />
<Resource name="jdbc/mydb" type="javax.sql.DataSource"... />
-->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<Manager pathname="" />
</Context>
Use a deployment outside of the webapps directory, for example in wars/
Create an XML file ROOT.xml under the conf/Catalina/localhost/ that define the docBase attribute with a path relative to the webapps directory :
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="../wars/MyApp.war"></Context>
In this way :
the conf/server.xml file is not modify
the name of the war file is not necessary ROOT.xml
resources are defined in conf/context.xml
But :
you must have one file in conf/Catalina/localhost/ per .war
wars are not auto-deployed
if you change the name of the root war file, you must modify the docBase attribute in conf/Catalina/localhost/ROOT.xml file.

SEVERE: Begin event threw error java.lang.ExceptionInInitializerError Intellij/Tomcat

I got a new project which I wanted to work with in Intellij. As local server we use Tomcat 7.0.68 and JDK 1.8.
This is my configuration in tomcat.
lib/catalina/org/apache/catalina/startup/Authenticators.properties:
NIGHTSHIFT=com.glit.swidA9O.v1.authenticator.NightShiftAuthenticator
conf/catalina.properties:
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,/Users/Administrator/dev/server/apache-tomcat-7.0.68/shared/nightShift/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
conf/context:
<Valve className="com.glit.swidA9O.v1.authenticator.NightShiftAuthenticator" changeSessionIdOnAuthentication="true"/>
<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
virtualClasspath="/Users/Administrator/dev/PROJECT/config;/Users/Administrator/dev/PROJECT/data"/>
conf/server.xml
<Engine name="Catalina" defaultHost="localhost">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<Realm className="com.glit.swidA7U.v1.realm.NightShiftRealm" roleClassNames="com.glit.swidBS0.v1.shared.principals.NightShiftGroup" userClassNames="com.glit.swidBS0.v1.shared.principals.NightShiftUser"/>
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
Our problem
Inside our local tomcat server we want to use a specific library called "NightShift". Unfortunately every time we try to build our application with intellij/tomcat we get this error somehow:
Complete stacktrace
Which is funny because if we try to build the application with eclipse tomcat runs smoothly and can actually build the project properly.
Those are the settings for Intellij/Tomcat we made:
These are my vm-options (actually I don't even know if you need this but just in case):
-DLOG4J2-ROOT=/Users/Administrator/dev/PROJECT/data/logs
-Djava.security.auth.login.config=/Users/Administrator/dev/PROJECT/config/NightShift-config/common/NightShiftJAAS.conf
-Dpu8.config.path=/Users/Administrator/dev/PROJECT/config/NightShift-config
-Dpu8.configuration.id=A7U
-Dpu8.environment=ide
-Djava.util.logging.config.file=/Users/Administrator/dev/PROJECT/config/NightShift-config/log-config.properties
-Djava.security.manager
-Djava.security.policy="/Users/Administrator/dev/server/apache-tomcat-7.0.68/conf/catalina.policy"
This is what our tomcat looks like:
Inside "shared" is a folder called "nightshift" and there are all the neccessary .jar files which we mentioned in the catalina.properties file.
Inside our artifacts we didn't include the "nightshift" jars, cause we want it installed in tomcat and not in our application.
There appears to be a problem with your application's logging initialization:
Caused by: java.util.NoSuchElementException
at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:365)
at java.util.ServiceLoader$LazyIterator.access$700(ServiceLoader.java:323)
at java.util.ServiceLoader$LazyIterator$2.run(ServiceLoader.java:407)
at java.security.AccessController.doPrivileged(Native Method)
at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:409)
at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
at com.glit.swidPV4.v1.common.log.LogFactory.getInstance(LogFactory.java:32)
at com.glit.swidPV4.v1.common.log.LogFactory.getLog(LogFactory.java:42)
at com.glit.swidA7R.v1.businessdelegate.VerifierBDFactory.<clinit>(VerifierBDFactory.java:29)
... 75 more
It looks like it is looking for a service provider that doesn't exist, and then failing to deal with that properly. That leads to a runtime exception which cascades into other things.
Look at the source code for com.glit.swidPV4.v1.common.log.LogFactory to figure out what what it is looking for.
Figure out why it is missing. My guess would be a missing / misplaced JAR file or a missing / misplaced config file.
Fix it.
(Maybe) fix the LogFactory code to be more resilient ... or to throw a custom exception rather than just bombing out with an obscure NoSuchElementException.
Figuring out remotely where JAR and config files should be for your particular case is too difficult. But this is just standard Java / Tomcat troubleshooting ... once you have figured out what is causing the problem.
Unfortunately every time we try to build our application with intellij/tomcat we get this error:
One "solution" would be to switch to using Maven or something to build WAR files, and test / deploy them by hand rather than relying of the "shiny" Intellij integration stuff.
(You shouldn't be hot deploying into a production server anyway. That's poor practice. You are one mistake away from trashing your production env. And if this is just a dev / test environment, it is a good idea to get into the habit of doings things the right way in dev.)

Dynamic Wildcard settings in Tomcat Server.xml (Non www)

For example.com, the appBase for
a) www.example.com & example.com is /home/example/public_html/e - A wordpress site.
b) any other *.example.com is /home/example/public_html - A Java web app.
To achieve this, in server.xml, I am maintaining the following
a) For www & example.com
<Host name="example.com" appBase="/home/example/public_html/e" ...>
<Alias>www.example.com</Alias>
...
</Host>
b) For other wildcards, the following is NOT WORKING
<Host name="*.example.com" appBase="/home/example/public_html" ...>
...
</Host>
So, as a workaround, I have to MANUALLY ADD this whenever a,b,c etc are dynamically registered by the customers. Everytime requiring a Tomcat restart.
<Host name="*.example.com" appBase="/home/example/public_html" ...>
<Alias>a.example.com</Alias>
<Alias>b.example.com</Alias>
<Alias>c.example.com</Alias>
...
</Host>
MY QUESTION
Since the wildcards are dynamically generated at client registration, how do I dynamically set in server.xml such that the manual entry & Tomcat restart can be avoided.
The only way I know how at the moment is to specify the default host in server.xml
<Engine name="Catalina" defaultHost="default-host">
and then later on in the file you can specify all requests to go to a specific host
<Host name="example-site">
<Context path="" docBase="/home/example/public_html/e" />
<Alias>example.com</Alias>
<Alias>www.example.com</Alias>
</Host>
<Host name="registered-customers">
<Context path="" docBase="/home/example/public_html" />
<Alias>default-host</Alias>
</Host>
Good luck :)

Tomcat: status code 404 on requests of every application

Every application deployed on my Tomcat returns status code 404 on every request I make. I've tried several projects, helloworlds or skeletons and every project behaves the same as others:
Some of the projects I've used:
https://github.com/mwarman/skeleton-ws-spring-boot
https://spring.io/guides/gs/rest-service/ (I can run it with mvn spring-boot:run but it's not working as deployed to Tomcat)
https://github.com/shagstrom/spring-mvc-hibernate-skeleton
I'm using the newest Tomcat8 (8.0.27), Oracle JDK 8. I've also tried Tomcat7 with OpenJDK 7.
Used Tomcats are running on MAC and Debian.
I've build apps via mvn and via Intellij Idea.
Tomcats are clean, no configs are changed (except adding manager-gui user).
Since I've tried many different projects, I don't believe the problem is in the code. Is it in Tomcat's config? How can I get it to work?
EDIT:
server.xml code:
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
If you installed tomcat successfully, you may have seen tomcat main page from browser http://localhost:8080/ URL. (Otherwise, you need to check tomcat installation first.)
I guess you may have built jar by fallowing above sites that you mentioned.
I am wondering whether you applied the jar into your tomcat or not because you mentioned same result from any other projects.
You need to apply jar or war into your tomcat in order to apply java program.
Here are links that I wish they might be helpful to you.
Packaging war : How to make war file in Eclipse
Tomcat - war file deployment : https://www.youtube.com/watch?v=9X9DA8oVodk
If tomcat returns 404 response, you have to check those points:
check if your deploy was correct
check if war bootstrap and its ServletContext initialization has no errors
check if the resource your invoking was successfully exposed.
To test these issues you have to expose:
static resource test
web service resource test
in this way you can exclude the pitfalls you can meet during application deploy

connecting to derby database with tomcat as the server

How do i connect to derby database (that comes with the netbeans) ? I am using Tomcat as the server. Earlier i used the following statements to connect to the derby database,but then i used glassfish as the server.
Context context = new InitialContext();
DataSource ds = (DataSource)context.lookup("java:comp/env/jdbc/PollDatasource");
Connection connection = ds.getConnection();
But now using Tomcat as the server i am unaware how to do this.
Note : Tomcat and Derby are pre installed with netbeans IDE that i am using currently
In Tomcat find conf/context.xml, then edit and write something like this:
<Resource name="jdbc/PollDatasource" auth="Container" type="javax.sql.DataSource"
driverClassName="com.YourDriver"
url="jdbc:derby://localhost:1527/nameOfTheDatabase;create=true"
username="username" password="password" maxActive="20"
maxIdle="10" maxWait="-1" />
Note 1: With the above URL the driver will be org.apache.derby.jdbc.ClientDriver
Note 2 : You can also add the above information in META-INF/context.xml of your project. This becomes application specific.If you add the information in tomcat's context.xml that becomes global.
Note 3: Download the jar from this website.Download db-derby-10.9.1.0-bin.zip.It contains many files, including derby.jar and derbyclient.jar (along with much documentation).derbyclient.jar contains our friend org.apache.derby.jdbc.ClientDriver.class. derby.jar contains org.apache.derby.jdbc.EmbeddedDriver. Keep the downloaded jar in lib folder of Tomcat.
and in your application web.xml "resource-ref":
<resource-ref>
<description>my connection</description>
<res-ref-name>jdbc/PollDatasource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
You may want to look at these questions :
Isn't it necessary to mention the name of archive in the Resource tag?
When is the tag I added in context.xml gets read?
What are steps followed in the look-up? what is looked first web.xml or context.xml?
You need to:
1) Copy your derbyclient-*.jar to ${TOMCAT_HOME}/lib.
2) Edit your server.xml and add the following lines to the section GlobalNamingResources:
<Resource auth="Container"
driverClassName="org.apache.derby.jdbc.EmbeddedDriver"
maxActive="8" maxIdle="4"
name="jdbc/my-ds" type="javax.sql.DataSource"
url="jdbc:derby:mydb;create=true"
username="myuser" password="mypassword" />
3) In your context definition, add:
<Context docBase="myapp"
path="/myapp"
reloadable="true"
...>
<ResourceLink name="jdbc/my-ds"
global="jdbc/my-ds"
type="javax.sql.DataSource" />
</Context>
4) Restart Tomcat.
The example you have requires JNDI. See the relevant tomcat versions docs on setting that up.
Or use a connection string, here's a page from derby docs http://db.apache.org/derby/integrate/plugin_help/derby_app.html

Categories

Resources