Best practice? JNDI, Hibernate and Tomcat - java

I've got a web application, hosted with tomcat, which uses hibernate to talk to a database.
I'm looking at how I can easy the pain of configuration as I migrate from dev, to test and to prod.
I've seen JNDI mentioned a lot and at first glance it seems like a good idea. You configure a jndi resource on each tomcat instance and the web context just uses it.
However after examining it further it seems that in order to have a JNDI I've got to have all my database objects + hibernate in the tomcat lib files in order for this to work. This sounds scary to me, what if I want to deploy another context that uses a different version of hibernate?
Also, am I not just swapping the pain of maintaining configuration for the pain of breakages caused by mismatches between the installed jndi resource classes and the ones in my context.
Ideally I think what I'm wanting is to just say in tomcat. There is a database called X, it is at this server and has this user/pass.
I'd appreciate your thoughts on the best way to handle the need for different config in different environments without having an extra step after each deploy to update the config files.
Cheers,
Peter

You have confused things a bit, I believe.
JNDI is just a name assigned to a datasource pool. This datasource uses a JDBC driver which in global Tomcat classpath, but that about the only shared resource in the whole setup.
Datasource has connection URL, username, password and options for connections defined, which may differ per server, but application doesn't care about it -- all it knows is the JNDI name, e.g. "jdbc/myDatasource".
All hibernate JARs, and well as any other JARs and whats not are to be packaged within the WAR. They are "visible" only within the WAR, and therefore you can have multiple applications using conflicting versions of libraries deployed to the same Tomcat.
No need to pollute lib/ directory of Tomcat. This is a bad practice, as you correctly observed.

Related

External web-application configuration in Tomcat

There's a web application and a number of environments in which it works. In each environment it has different settings like DB connection and SOAP ends-points that in their turn are defined in properties-files and accessed in the following way:
config.load(AppProp.class.getClassLoader().getResourceAsStream(
PROPERTIES_FILE_PATH + PROPERTIES_FILE_NAME));
Thus the WAR-files are different for every environment.
What we need is to build a unified WAR-file that doesn't contain any configuration and works in any environment (for now, Tomcat instance) getting its configuration from outside its WAR-file.
The answer Java Web Application Configuration Patterns, to my mind, gives the full set of common approaches but with just few examples. The most attractive way is configuring JNDI lookup mechanism. As I can guess it allows to separately configure web-applications by their context paths. But couldn't find a simple (step-by-step) instructions in both the Internet and the Tomcat's docs. Unfortunately cannot spend much time on studying this complicated stuff in order to just meet so seemingly simple and natural demand :(
Would appreciate your links at the relevant descriptions or any alternative suggestion on the problem.
If its a case of simply deploying your WAR on different environment (executed by different OS user), then you can put all your config files in the user's home folder and load them as:
config.load(new FileInputStream(System.getProperty("user.home") + PROPERTIES_FILE_NAME));
This gives you the isolation and security and makes your WAR completely portable. Ideally though, you should still provide built-in default configuration if that makes sense in your case.
The approach we've taken is based on our existing deployment method, namely to put the WAR files in the filesystem next to the Tomcat, and deploy a context.xml pointing to the WAR file to Tomcat.
The context descriptor allows for providing init parameters which is easily accessible in a servlet. We've also done some work on making this work with CDI (for Glassfish and TomEE dependency injection).
If you only have a single WAR file deployed to this Tomcat instance, you can also add init parameters to the default global context XML. These will be global and you can then deploy the WAR file directly. This is very useful during development.

How do I have multiple datasources with the same JNDI name in JBoss?

So, I have a situation where I will be deploying multiple ear files, each of which I need to configure with a different database (potentially).
Right now I have a *-ds.xml file that is deployed in JBoss, with a JNDI name that all my portlets and servlets use to look up the database connection. If I deploy more than one ear file with a *-ds.xml file that points to a different database, but with the same JNDI name, the deployment barfs. Shouldn't JNDI be more modular to prevent naming collision when deploying different application to isolate them.
It will take quite some time to change my lookup in the code, so is there a way to deploy that will work? The only other idea I had would be to set a JNDI property as the value for the datasource name to look up, and then look up that JNDI property first and retrieve the datasource based on the retrieved "key." This seems kind of hackish to me...
You can have the different data source defined on the application server with different name (on the server namespace) and then map it to a resource-ref name (this time component namespace, so each application can have its own one) via the deployment xml file.
For JBoss prior as7: https://community.jboss.org/wiki/HowDoICreateAResourceRef for as7: https://community.jboss.org/message/629666

How to deploy the same web application twice on WebLogic 11g?

We have developed a JEE5 web application (WAR) and running it in production under WebLogic 11g (10.3.5).
Now the same application should be deployed as separate applications for different customers (different URLs, different data) on the same WebLogic.
I managed the first part by setting different context roots after deployment for each of them.
But I have yet to make them use different datasources - and since I want to avoid customer specific builds, the persistence.xml is the same for all applications, thus also the persistence unit name.
What is the best setup for this scenario? Am I forced making separate builds and by that different WARs or do I have to separate Managed Servers or Domains wihtin the server or is there a better way to solve it?
I know this thread is very old,but replying so that it may help someone with the same question stumbling on this thread.
The latest weblogic 12.2.1 comes with Multi-tenancy(add-on I guess) which can let you run same applications in a single domain.
Edit: Weblogic 12.2.1 introduced concept called Partitions. Partitions are both config and run-time subdivision of a weblogic Domain. In a single weblogic domain you can create multiple partitions. Each partition will have one or more resource groups. Resource groups are the logical grouping of weblogic resorces like data sources,jms,Java EE apps ,etc. For example to achieve what the original posts asked for , we create a Resource Group template with the web-application and the datasource as the resources. In the Data source configuration we can provide a place holder variable instead of actual URL as DB URL. Then we can create two partitions that refers to this Resource Group Template(Each partition will now have a separate web application and data source) . Each partition will override the DB URL property there by creating two data sources with same JNDI name.In each Partition we create virtual host/port so that the client can use that to access the application running in the respective partitions.
A better and more detailed information on this can be found in https://blogs.oracle.com/WebLogicServer/entry/domain_partitions_for_multi_tenancy
ServletContextListener.contextInitialized can look at the ServletContext and figure out which deployment is which
in web.xml, define a servlet context listener:
<listener>
<listener-class>com.path.YourServletContextListener</listener-class>
</listener>
and then in YourServletContextListener.java, add a contextInitialized method like this:
public void contextInitialized(ServletContextEvent sce)
{
ServletContext sc = sce.getServletContext();
String name = sc.getContextPath();
...
}
my thought is that you can use that name to select from multiple data sources that you have configured. depending on how you've been deployed, you'll make a different database connection and have the correct application's data.
It seems to me from what I saw in the Oracle documentation, that having several domains is the only way to separate data sources with the same persistence unit name - which is bad, since this basically means running two WLS in parallel.
For this reason I decided to go with building individual WAR files (which I tried to avoid initially), to include customer-specific persistence.xml files and specifying customer-specific datasources in the WLS.

Custom resource in JNDI on different application servers

Preface:
Most of J2EE applications are using container managed datasources through JNDI. This is fine as it gives one place for configuring these connections.
The problem arises when we want to use ORM framework (like hibernate) or something that have to know the default schema (mostly for Oracle, may be others too), which can be different from the username that is used to connect to the DB.
I want to put the default schema name somewhere close to the datasource definition. One of the options would be to put it in JNDI. I will then manually read of from there before construction the EntityManager (well actually using Spring).
As I found out there is a simple way to specify custom resource (in this situation it will be String with default schema name) in Apache Tomcat like this (correct me if I'm wrong):
<Environment name="schemaNames/EmployeeDB"
type="java.lang.String"
value="empl"
description="Schema name of Employees Database for HR Applications"/>
Anyway, considering this can be done in Apache Tomcat, how should I configure the same custom JNDI resource (of String type) within other application servers:
JBoss 4/5
WebSphere 6/7
WebLogic 9/10
If you know about other servers that would be great too.
Also, as an alternative I don't want to put the schema name in system properties or environment variables.
Thank you very much !
Update:
Found some way of achieving it on JBoss. I didn't test it tho.
http://forums.java.net/jive/thread.jspa?messageID=316228
Found information for WebLogic, but they talk about doing it programmaticly and not with configuration:
http://weblogic-wonders.com/weblogic/2010/06/12/binding-objects-in-weblogic-servers-jndi-tree/
http://forums.oracle.com/forums/thread.jspa?messageID=4397353
For WebSphere you can actually set the default schema in your defined DataSource. It is a custom property called currentSchema. (ie, in V7 it is Resources > JDBC > Data sources > your data source name > Custom properties > currentSchema.
Otherwise you can use a Name Space Binding and define it there: (ie, in V7 it is Environment > Naming > Name Space Bindings. You can use JNDI to look this up if you don't want to programmatically set it in WebSphere.
Can't speak to JBoss and WebLogic as I haven't worked with them.
If you are using Hibernate, this is the property to add in persistence unit :
<property name="hibernate.default_schema" value="myschema" />
That is the prefix that JPA will insert for table names.
If you need something 'closer' to the AS Datasources definitions, you may inject some DB-specific SQL at DB connection time; for instance Oracle,
ALTER SESSION SET CURRENT_SCHEMA =
On JBoss, you may add this in the datasource definition :
<new-connection-sql>
ALTER SESSION SET CURRENT_SCHEMA=myschema
</new-connection-sql>
Also editable in JBoss 7 Admin.
On Weblogic, you may inject this in the Connection Pools.
On Websphere, this should be similar.
On JBoss, you can use a special MBean(org.jboss.naming.JNDIBindingServiceMgr) and a service.xml to configure JNDI-entries, and then map these entries into your webapps. There is a lengthy explication for this rather non-trivial process here:
http://usna86-techbits.blogspot.com/2011/01/jboss-jndi-and-javacompenv.html
I'm still looking for a a way to place an entire properties-file/resourcebundle into jndi, as this manual mapping gets very tedious when you have a lot of properties that you want to put into jndi and make available for your webapps.
This same problem has been bothering be for quite a while for WebLogic, in particular 10.3.5 (11g).
I spent most of a day looking around and all I found was this: http://code.google.com/p/weblogic-jndi-startup/. It works just fine. It is a little restrictive: it requires the object you want to add to JNDI to have a constructor with a single String parameter.
For what I needed, weblogic-jndi-startup didn't work, so I built on Roger's code and came up with this: https://bitbucket.org/phillip_green_idmworks/weblogic-jndi-custom-resource-configuration/. I have a write up for it at http://coder-in-training.blogspot.com/2012/03/weblogic-jndi-custom-resource.html

is there a standard way to define a JDBC Datasource for Java EE containers?

I know that for JBoss you need a [name]-ds.xml file in the /deploy subdirectory of the appropriate instance.
i dont have any experience with other Java EE containers, but im trying to stick to standards as much as possible.
is there a standard way to define a JDBC datasource and deploy it ? if possible i'd like to include my datasource inside the *.ear file (for instance, an embedded in-memory HSQLDB datasource for demo purposes) ?
if there is no standard way, will other containers at least accept the jboss way ? (/deploy/*-ds.xml)
Is there a standard way to define a JDBC datasource and deploy it?
Yes, there is. It's done via the <data-source> element, which you can put in web.xml, ejb-jar.xml and application.xml. If you don't like XML, you can also use an annotation for this instead: #DataSourceDefinition
Example of a web.xml entry
<data-source>
<name>java:app/myDS</name>
<class-name>org.postgresql.xa.PGXADataSource</class-name>
<server-name>pg.myserver.com</server-name>
<database-name>my_db</database-name>
<user>foo</user>
<password>bla</password>
<transactional>true</transactional>
<isolation-level>TRANSACTION_READ_COMMITTED</isolation-level>
<initial-pool-size>2</initial-pool-size>
<max-pool-size>10</max-pool-size>
<min-pool-size>5</min-pool-size>
<max-statements>0</max-statements>
</data-source>
Further reading:
Introducing the DataSourceDefinition Annotation
The state of #DataSourceDefinition in Java EE
Example application use standard data source
p.s. I'm surprised all other answers say this doesn't exist, while it clearly does, even at the time this question was originally asked.
Is there a standard way to define a JDBC datasource and deploy it ?
No, this is container specific. As Application Component Provider, you're supposed to document the resources you need and the Application deployer and Administrator will configure them.
If there is no standard way, will other containers at least accept the JBoss way?
No, because this is the JBoss way and thus JBoss specific.
With Tomcat, you would have to use the context.xml file.
With Jetty, jetty-env.xml.
With WebSphere, you can create a so called WebSphere Enhanced EAR.
With WebLogic, you can package a JDBC Module in your application.
With GlassFish, you can use the command asadmin add-resources my.xml to add a datasource described in a XML file (example here).
Etc, etc.
Note that there are some projects trying to achieve this goal in a universal way like jndi-resources or Cargo. There are also more complex solution like ControlTier or Chef.
Now, in your case (as I understood you want to use an embedded database that will be bundled with your application), I don't think you should configure a datasource at the application server level. You should just package the jar of your database in your application with a standalone connection pool like c3p0 or DBCP.
Sun's Java EE philosophy defines several roles in the design, development and deployment of an enterprise application. Java EE design accommodates and reflects these separations of concerns.
In particular Sun wants to separate the developer from the administrator of an application, which is a good idea. The developer writes enterprise components in a container-agnostic way. In web.xml, for example, you do declare your DataSources in a standard way:
<resource-ref>
<res-ref-name>jdbc/myDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
This says "this database thing the application needs, make it available to me, whatever database is and whatever container you're running it in, via standard JNDI at 'jdbc/myDB' ". This is as much as the developer can do -- the rest is necessarily container specific and therefore not standardized.
And then how "myDB" is actually configured is up to a different role, the administrator of the container.
So I'm repeating the correct answer above: no. But the reason is, otherwise, you'd be coding your app to a specific type of database on a specific host and port, and the point is that you shouldn't be able to do that, so there's no standard support for that on purpose.

Categories