Modifying a JNDI connection pool programmaticaly - java

I am using Apache Tomcat JDBC connection pool library in my project and configured the context.xml file accordingly. My application instance needs to run at multiple locations, but load on the application will be different, so I want to modify the maxActive size and some other property based on the customer size at particular instance at runtime.
<Context path="/abc"
docBase="abc"
debug="5"
reloadable="false"
crossContext="true">
<Resource name="jdbc/abc"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="xxxxx"
url="xxxxxxx"
username="xxxxx" password="xxxxxx"
maxActive="20"
initialSize="0"
...
/>
</Context>

There is nothing special about a datasource created through JNDI: if you know its class (org.apache.tomcat.jdbc.pool.DataSource in your case), you can cast to that class and use the available setters to configure it:
private void customizeDataSource(final DataSource ds) {
if (ds instanceof PoolConfiguration) {
final PoolConfiguration poolConfig = (PoolConfiguration) ds;
poolConfig.setMaxActive(10);
}
}
(see the definition of PoolConfiguration). Implementations of javax.sql.DataSource also implement a very useful interface Wrapper, which may come handy if your code wraps the Tomcat JDBC datasource in something else:
private void customizeDataSource(final DataSource ds) throws SQLException {
if (ds.isWrapperFor(PoolConfiguration.class)) {
final PoolConfiguration poolConfig = ds.unwrap(PoolConfiguration.class);
poolConfig.setMaxActive(10);
}
}
There are however some problems that can arise from the programmatic approach above:
if you bundle tomcat-jdbc.jar with your application, only JNDI resources configured in your context.xml will be recognized by your code. Those in GlobalNamingResources will use the copy of org.apache.tomcat.jdbc.pool.DataSource bundled with Tomcat and will not match the instanceof condition.
if, on the other hand, you don't include tomcat-jdbc.jar into your WAR file, you must make sure that the parameters you set are supported by all versions of Tomcat on which your application will run.

You could try using standard JMX for this purpose.
As you can see in the documentation, Tomcat can expose the connection pool as a MBean object you can interact with using tools like JConsole, for instance.
The the MBean implementation basically delegates to the actual org.apache.tomcat.jdbc.pool.ConnectionPool the different operations that can be performed through the MBean interface and, AFAIK, ConnectionPool dynamically allocates connections based on current configuration.
ORIGINAL ANSWER BASED ON CONFIGURATION AND NOT RUNTIME BEHAVIOR
Please, consider review this related SO question and the self provided answer, I think it could be helpful.
Tomcat substitutes system provided environment variables in its configuration files:
Tomcat configuration files are formatted as schemaless XML; elements and
attributes are case-sensitive. Apache Ant-style variable substitution is
supported; a system property with the name propname may be used in a
configuration file using the syntax ${propname}. All system properties are
available including those set using the -D syntax, those automatically
made available by the JVM and those configured in the
$CATALINA_BASE/conf/catalina.properties file.
As indicated, the best way you could include the properties you need to dynamically being substituted by Tomcat is passing them as system properties using the -D option and probably in the JAVA_OPTS environment variable.
As indicated in the afore mentioned question, and advised as well in catalina.sh:
# Environment Variable Prerequisites
#
# Do not set the variables in this script. Instead put them into a script
# setenv.sh in CATALINA_BASE/bin to keep your customizations separate.
#
define them, for example, in a setenv.sh file located in the $CATALINA_BASE/bin directory.
For example:
#! /bin/sh
export MAX_ACTIVE_CONNECTIONS=20
export JAVA_OPTS="$JAVA_OPTS -DmaxActiveConnections=$MAX_ACTIVE_CONNECTIONS"
And use these properties in your XML configuration files:
<Context path="/abc"
docBase="abc"
debug="5"
reloadable="false"
crossContext="true">
<Resource name="jdbc/abc"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="xxxxx"
url="xxxxxxx"
username="xxxxx" password="xxxxxx"
maxActive="${maxActiveConnections}"
initialSize="0"
...
/>
</Context>

MySQL connects rapidly, thereby making connection pooling of limited use.
Usually, if there is a performance problem it is better handled by other techniques -- composite indexes, reformulating queries, working around optimization limitations of MySQL, etc.
Would you care to back up a step and let's analyze the bottlenecks?

Related

Explanation of context.xml

When using Tomcat, I've always treated web.xml as a kind of .htaccess or httpd.conf equivalent. It seems natural that there might have to be some way of configuring a web server.
However, I don't quite understand the purpose of context.xml. For instance, when working with JDBC, why do I have to add a resource-ref in web.xml and also a Resource with even more info in context.xml? Could I eliminate the context.xml file and somehow instantiate the DataSource in my code? I am asking because hypothetical examples like that help me understand.
EDIT: I am trying to understand what is happening in configs like this, in /META-INF/context.xml:
<Context>
<Resource name="jdbc/postgres" auth="Container" type="javax.sql.DataSource"
driverClassName="org.postgresql.Driver" url="jdbc:postgresql://127.0.0.1:5432"
username="postgres" password="yes" maxActive="20" maxIdle="10"
maxWait="-1" />
</Context>
and, in WEB-INF/web.xml:
<resource-ref>
<description>postgreSQL Datasource example</description>
<res-ref-name>jdbc/postgres</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Why do I have to put both of those in there to use JDBC? What are they doing exactly and is there another way of doing the same thing but in the Java code? Like I said, not because I want to, but because I want to understand what they are doing better.
I don't quite understand the purpose of context.xml
context.xml is called from the web.xml through <context-param> tag. As you web.xml loads first when an application is created and it will create the references for the contexts that are configured in it.
I don't quite understand the purpose of context.xml
So, the purpose of context.xml is adding separation of codes. you can have separate contexts for different purposes . for example For Database connectivity, using other frameworks etc..,
Could I eliminate the context.xml file and somehow instantiate the DataSource in my code?
Yes ,you can do that by configuring the datasource in web.xml itself.
Hope this helps !!
To your initial questions:
I don't quite understand the purpose of context.xml
In Tomcat, you'll frequently see the term "Context". When you see this it's just referring to your application (i.e. Context == your web app). Thus /META-INF/context.xml is the configuration file specific to your application (actually, it's one of the configuration files because there are others).
For instance, when working with JDBC, why do I have to add a resource-ref in web.xml and also a Resource with even more info in context.xml?
You don't. You do not need to add anything to web.xml. If you define the resource in /META-INF/context.xml, Tomcat will create your resource and expose it through JNDI. You can then retrieve it as you would any resource from JNDI.
Could I eliminate the context.xml file and somehow instantiate the DataSource in my code?
Possibly. It's common to see Spring users create a DataSource in their Spring bean definitions. If you're not using Spring, you could still do it, but it would be more work.
Regardless of how you do this, if you setup the DataSource in your application you lose some flexibility. For example, you cannot share the DataSource across multiple applications and your application has to know how to set up the DataSource. If you define the DataSource in the server's configuration (i.e. with a Resource tag) then your application does not need this information.
To the response by san krish:
So, the purpose of context.xml is adding separation of codes. you can have separate contexts for different purposes . for example For Database connectivity, using other frameworks etc..,
The purpose of the Context tag is to configure the context (i.e. your application). It might provide you with the ability to pull certain aspects of your application, like the DataSource, out of the code and into configuration, but that's just the benefit of externalizing your configuration. It's not the purpose of this file.
Yes ,you can do that by configuring the datasource in web.xml itself.
No, with Tomcat you cannot configure a DataSource strictly through web.xml. It's just not possible to provide all of the information in web.xml that is required to create a DataSource. Because of this, you need to define a Resource tag for your DataSource and it is redundant to specify your DataSource in web.xml.

How does Tomcat load resources from context.xml

Hoping that someone can clear up a few things -
For a project I need to be able to switch between a Sybase and an Oracle database. Using Spring I have been to come up with a solution using AbstractRoutingDataSource to allow switching between the sybase and oracle data source as needed.
However, within my tomcat context.xml, I have listed all data sources - so for each one I have "jdbc/myDbSybaseDataSource" and "jdbc/myDbOracleDataSource". I was wondering, does tomcat attempt to create all of these data sources on start up, or does it wait until something in the code has invoked it?
I know in spring you can lazy load your beans, but would tomcat still create each resource in the context.xml anyways?
Any help is much appreciated!
Edit
The reason for asking is - if for example, the Sybase database is down, we need to know that we can switch to the Oracle, but Tomcat will not try and load the Sybase resource from the context.xml at start up (unfortunately I am not in a position to turn off one of the databases to test this! :) ).
From what I can tell, Tomcat is loading the resources - but not trying to actually connect to the database until part of the code invokes the data source, at which point Spring kicks in and does its work. It simply loads in the resources and keeps the info stored somewhere, when Spring tries to create the data source it looks up the corresponding resource info and uses this
Yes, Tomcat will create these datasources at startup. I cannot tell you how far this initialization goes but I would try to avoid any overhead that you dont need.
Do you need both datasources at runtime or is it just to be able to decide at startup of your application?
In the second case you can see the cotext.xml file as an (external) configuration file. You can then use springs jndi reference feature to connect to the desired database for the current application.
In the root-context.xml you reference ONE datasource by name:
<jee:jndi-lookup id="mvcDatasource" jndi-name="jdbc/mvcDatasource"
expected-type="javax.sql.DataSource" />
And depending on which type should be used you declare the correct implementation in the context.xml:
<Context>
<Environment name="configDirectory" type="java.lang.String" value="file:///tmp/app1" />
<Resource name="jdbc/mvcDatasource" type="javax.sql.DataSource" auth="Container" driverClassName="org.h2.Driver" maxActive="8" maxIdle="4" username="user" password="" url="jdbc:h2:file:/tmp/app1/db" />
</Context>
As you can see you can use other declarations too. In my case i inject the external config location here to be able to load properties files for the propertyconfigurer too. So this location will be application specific.

JDBC connection String in AWS Elastic Beanstalk

i want to migrate my applicaztion in aws,
i have setup my env. using elastic beanstalk.
in current version of my application, it runs OK locally, i setup the connection with this simple code:
in web.xml i have defined the resource:
<resource-ref>
<description> Resource reference to a factory for java.sql.Connection instances that may be used for talking to a particular database that is configured in the <Context> configuration for the web application. </description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
then in config.xml i defined the resource
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="utente1" password="utente1" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/nazioni?autoReconnect=true" />
now i'm able to get the connection in this way
/* JNDI query to locate the DataSource object */
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env") ;
// JNDI standard naming root
DataSource ds = (DataSource)envContext.lookup("jdbc/TestDB");
/* Ask DataSource for a connection */
Connection conn = ds.getConnection();
now my question is: can i hard code the connection string in my config.xml or it is better to use System.getProperty("JDBC_CONNECTION_STRING")? if so, how i can set the connection string when tomcat is loaded ?
thanks, Loris
Tomcat Configuration Reference mentioning support for Ant-style variable substitution:
Apache Ant-style variable substitution is supported; a system property
with the name propname may be used in a configuration file using the
syntax ${propname}. All system properties are available including
those set using the -D syntax, those automatically made available by
the JVM and those configured in the
$CATALINA_BASE/conf/catalina.properties file.
It means that if you define a property within .ebextensions/<environment-name>.config such as:
option_settings:
- option_name: JDBC_CONNECTION_STRING
value: jdbc:mysql://localhost:3306/nazioni?autoReconnect=true
Then you should be able to reference that property in the configuration files:
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="utente1" password="utente1" driverClassName="com.mysql.jdbc.Driver" url="{JDBC_CONNECTION_STRING}" />
The approach has at least two benefits over hardcoded:
The value can be changed manually in AWS Beanstalk Console on a running instance
The value can be changed programmatically on a running instance
Better go with XML configuration Becasue most of the application when moved from one Environment to another.There will be change in XML probably so XML is better from my Experience.
Also you can use property file but which helps most of the time when you want some property which is to be changed very often like lables not like configuration based things DB Connection String,SMTP,etc.
The Name of WEB.XML is Deployment descriptor becasue at the time deployment which is useful to load details about Applications based on the different environment.
In general, it is best NOT to hard-code connection string information within your application (or other similar application configuration). Ideally you want to externalize application configuration so that your application to be portable between environments.
In the case of Elastic Beanstalk, you could deploy the same application WAR in different Beanstalk environments at the same time (perhaps a QA and a PROD environment) and if you hard-code the connection string then both WAR files would be pointing to the same DB instance.
Similarly, you may need to change the connection string at some point, maybe because your DB instance name changed, and if you hard-code the connection string then you would have to rebuild and deploy a new JAR.
If you use the JDBC_CONNECTION_STRING configuration provided by Elastic Beanstalk and read these in at runtime, then you can easily change the connection string using the Elastic Beanstalk console or API, and you can also maintain portability for a single WAR between different environments.

How to read external properties files in WebSphere?

I developed a sample web application which will read the data from an external properties file. The properties file is in the source folder in my system and is not included inside the WAR file.
The property file is accessed like this:
Properties prop = new Properties();
//File f1 = new File("Property.properties");
prop.load(getClass().getClassLoader().getResourceAsStream("Property.properties"));
How do I access this property file externally inside the WAR file?
What changes have to be made in the code to read it in the WAR file?
I think the most versatile approach is to define a simple environment entry as described in the section EE.5.4 Simple Environment Entries of Java™ Platform, Enterprise Edition (Java EE) Specification, v5.
From the section (page 68):
A simple environment entry is a configuration parameter used to
customize an application component’s business logic. The environment
entry values may be one of the following Java types: String,
Character, Byte, Short, Integer, Long, Boolean, Double, and Float.
You may also use URL connection factory as described in the section EE.5.6.1.4 Standard Resource Manager Connection Factory Types of the specification.
The Application Component Provider must use the java.net.URL resource
manager connection factory type for obtaining URL connections.
Both require a definition of a resource reference in the deployment descriptor WEB-INF/web.xml of your web application so you can inject the value using #Resource or use JNDI API with java:comp/env as the entry point.
The benefit is that you can change the configuration of your web application without having to recompile the code as well as let you change it using an application server's administrative tools your admins are accustomed with.
In web.xml you define the resource reference.
<resource-ref>
<res-ref-name>propertiesURL</res-ref-name>
<res-type>java.net.URL</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref>
<res-ref-name>propertiesPath</res-ref-name>
<res-type>java.lang.String</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Then in your code you use the following to access the values:
#Resource
String propertiesPath;
#Resource
URL propertiesURL;
With this you met the requirements of Java EE and you can use propertiesPath or propertiesURL as if they were passed as input parameters to your methods.
Now, it's time to meet expectations of WebSphere Application Server.
What you defined are logical names that need to be mapped to their administered names (an application server knows about and can provide to the application).
In WebSphere Application Server you use WebSphere Binding descriptor WEB-INF/ibm-web-bnd.xml with the following configuration:
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_1.xsd"
version="1.1">
<virtual-host name="default_host" />
<resource-ref name="propertyURL" binding-name="propertyURL" />
<resource-ref name="propertyURL" binding-name="propertyURL" />
</web-bnd>
When the application gets deployed WAS allows you to map these mappings to its administered resources. Use the ISC console to define values of the environment entries and map them to the application.
It has became easier with WebSphere Liberty Profile. I described the mechanism as offered by WLP in my article Using #Resource to access JNDI in WebSphere AS 8.5 Liberty Profile.
You have three options:
configure the Websphere to include the directory which contains the property file in the classpath. Don't know how to do it, but I'm sure it is possible, since our application does the same thing
include the property file in the war archive. You probably don't want to do that.
instead using the classloader to load the property file use the file api with an absolute path. I'm not completely sure WAS does allow that, but it is a bad idea anyway, because it makes your application very dependent on things that it really shouldn't care about, such as the installation path of your application.
WebSphere has two folders on the classpath, properties can be loaded from there:
Enterprise Applications > myear > Manage Modules > myjar.jar > Class loader viewer
4 - Extension - com.ibm.ws.bootstrap.ExtClassLoader
file:/projekte/IBM/WebSphere/AppServer-8.5/classes/
file:/projekte/IBM/WebSphere/AppServer-8.5/lib/

Using a JNDI datasource created by another application with Tomcat

I have a .properties file in my application which contains dataSource properties.
I set up a JNDI reference to this dataSource using the following code :
// first I create MyDataSource from the properties found in the .properties file
//then :
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind(jdbc/oracle/myDataSource, MyDataSource);
If I use a lookup in this application, the dataSource is found :
Context initContext = new InitialContext();
BasicDataSource dataSource =
(BasicDataSource) initContext.lookup("jdbc/oracle/myDataSource")
//everything works fine and I can use my dataSource to getConnection,
//requests, etc...
Now I would like to use this dataSource in another application. But if I do the same lookup than previously, I don't find myDataSource (whereas there is still the previous application in tomcat and the jndi binding is done on start-up with the help of a listener).
How can I get myDataSource in this second application, given that I can't use a Tomcat's resource in server.xml or a context.xml file (for different reasons I have to use this .properties file)?
Thanks
"local" JDNI directories are read-only in Tomcat. Nevertheless, you can bind "global" JNDI resources in a LifecycleListener, and then "link" them to your context(s)(*):
You need to implement org.apache.catalina.LifecycleListener http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/LifecycleListener.html
Then register it in your server.xml like this (along with the other listeners):
<Listener className="yourlistener.YourLifecycleListener"/>
Your listener should await for 2 events:
public void lifecycleEvent(final LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
// Create your datasource instance...
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind("jdbc/oracle/myDataSource", myDataSource);
} else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
// unbind...
}
}
Then you'll have to propagate resource accesses by "linking" them from "global" JNDI directory to "local" JNDI directory using ResourceLink element in your META-INF/context.xml:
<ResourceLink name="jdbc/oracle/myDataSource" global="jdbc/oracle/myDataSource"
type="javax.sql.DataSource" />
That worked for me so far.
(*) Some notes:
There's an advantage on using lifecycle listeners. Since the order of context creation is not guaranteed. The advantage is that all of your contexts will see this object created.
If you need to create and configure datasource creation more dynamically that on lifecycle listener creation, note that you can bind a custom class implementing the Factory pattern.
To avoid classloading incompatibility problems, consider putting your listener, datasource, etc. classes in a jar file in the Tomcat lib directory, so they're included y the common classloader.
Regards.
What you are trying to do is not going to work. J2EE applications are not allowed to modify the JNDI environment provided by the application server (J2EE spec, section 5.2.2) and the Tomcat JNDI documentation also states, that each web applications gets each own read-only JNDI environment. I'm not sure why binding/rebinding your datasource is not failing immediately and why it's working within the same web application, but even such application-internal usage of the JNDI environment is undocumented behaviour, which I would not rely on.
A couple people have already commented on this, but I think the answer to your question is: Tomcat has a file called server.xml that you need to use. A good reference I have used before is below:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html
Resources defined here will be visible to all apps deployed (if set up correctly). If you set up a JNDI resource in your apps context or web xml file, it should only be available to your app.
JNDI context are private to each webapp. Context created in one app can't be accessed by others.
Try to create an entry in GlobalNamingResources and links in both webapps using <ResourceLink> to see if it works.
I used this setup before to read from both apps but never tried to write from one. So not sure if it will work.
Actually, it is possible to access others JNDI resources, if the servlet implements org.apache.catalina.ContainerServlet. This interface has a org.apache.catalina.Wrapper attribute, that is "populated" by the container itself.
through that, I created a simple application to monitor resources.
BUT, I would like to do that in a listener, so my resource monitor could start when the container starts. Anyone knows a way?

Categories

Resources