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.
Related
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?
there's some things I definitely don't understand while using JNDI.
This should simplify our Java developers' life, but this promise sounds false.
The simplest way tu use a database from within an application seems to build each time you need it (so, once per application) a datasource class giving you a connection or session you will use.
It's a tool class written in a few minutes where you have to declare the driver, url, login and password. And it's done.
The recommanded way is to rely on the server resources management, JNDI and all resources such as directory, etc.
But is it really usefull and simple ??
With Tomcat, you have to have to fully describe the datasource in server.xml, then declare it as a resource in context.xml.
server.xml
<GlobalNamingResources>
<Resource auth="Container" driverClassName="org.postgresql.Driver" maxIdle="30" maxTotal="100" maxWaitMillis="10000" name="jdbc/dsTriasEmployees" password="pwd" type="javax.sql.DataSource" url="jdbc:postgresql://localhost:5432/trias_employees" username="login"/>
</GlobalNamingResources>
context.xml
<ResourceLink name="jdbc/dsTriasEmployees" global="jdbc/dsTriasEmployees"
type="javax.sql.DataSource" />
Java side, you should be able to inject it thanks to a Context Lookup code ou a #Resource(mappedBy...) annotation.
DataSource datasource;
public Connection connectJNDI() {
try {
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
datasource = (DataSource) envContext.lookup("jdbc/trias_employees");
Connection con = datasource.getConnection();
genericLogger.info("JNDI LOOKUP -> " + con);
return con;
} catch (Exception ex) {
genericLogger.error("JNDI LOOKUP -> " + ex);
return null;
}
But after doing that, you still have to deal with error messages telling you that it's not enough just like this one :
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
I have read somewhere that I now should add a JNDI properties file and a declaration in the web.xml file like this :
web.xml
<resource-ref>
<description>
DB PostgreSQL
</description>
<res-ref-name>
jdbc/trias_employees
</res-ref-name>
<res-type>
javax.sql.DataSource
</res-type>
<res-auth>
Container
</res-auth>
</resource-ref>
But when I wanna test with jUnit, my web.xml is not usefull...
How many XML and properties files should I write just to respect the best practice that want me to delegate access to the datasource to the server ?
How many times will I have to duplicate all the informations related to url, pool connection, etc. ??
I've read 2 or 3 books on Java EE, 50 posts on StackOverFlow and dozens of forums and all the JNDI Resources HOW-TO on the Tomcat website.
But it still does not work...
Is there somewhere the full description of all I have to do to connect to my database using the JNDI process ?
Help me please, or I'll kill a kitten for each more minute I'm losing just to do my job in "the recommanded way".
Thanx by advance.
To test classes dependent on a DataSource or any other object provided by Tomcat via JNDI outside of Tomcat you could rely on TomcatJNDI. Point it to your server.xml and context.xml and it will initialize a JNDI environment with all the objects configured in the files.
TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processServerXml(serverXmlFile)
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.start();
Then lookup the datasource
DataSource ds = (DataSource) InitialContext.doLookup("java:comp/env/jdbc/trias_employee")
You can find out more about TomcatJNDI here.
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.
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.
what is benefit of defining db pool in tomcat vs in spring configuring file
<Resource name="jdbc/DBCPosPool" auth="Container" type="javax.sql.DataSource"
maxActive="30" maxIdle="10" maxWait="10000"
validationQuery="SELECT 1"
testOnBorrow="true"
username="xxx" password="xxx" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://abcd.com/test2?autoReconnect=true"/>
I can think of 3 benefits.
Your datasource can be reused by other web applications
Your datasource can be administered externally, independant from the web application. You never want to re-deploy code if the datasource changes. In big organisations the developer is usually not the person who administers the datasources and stuff.
Environment specific details. It's possible that you're working in a DTAP environments or something similar. The datasource can be different depending on your environment (development, production, ...). You don't want to maintain seperate Spring configuration files just for the sake of the data connection.
Defining pool in tomcat allows you to reuse this definition in others, non spring apps. Defining it in spring level helps you to deploy yor app including other containers.
If your application is packaged to a war file, defining resources in an application server allows you to modify the resources without rebuilding the application. All you need is to restart the application server. This is useful when deploying the same application on different environments (dev, test, prod etc).