Custom resource in JNDI on different application servers - java

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

Related

Share Hibernate connection with other libraries

I'm writing a web application in Java and use Hibernate to map the database with my java objects. In Hibernate I connect to the database using C3PO pool. Now I need to schedule some tasks. Herefore I will use Quartz. Now the scheduled tasks will be stored in the database. It is possible to share the connection pool of C3PO with Quartz? So that the database settings are in one and the same file and that only one library is responsible to open a databaseconnection.
This is sort of a subjective thing, but suffice it to say, if you want to share the connection you have a number of options.
If you are using spring, define the c3p0 connection pool as a bean and inject it into both the Hibernate session factory and the Quartz scheduler beans.
If you are trying to stay "pure" and not using Spring, you can define your a JNDI based data-source at the container level.
There are some nuances with both approaches.
Unfortunately without indicating how "portable" you are trying to be and what you are currently using beyond just 'java', 'quartz-scheduler' and 'c3p0' as tags an answer will be vague at best.
EDIT
Thanks to the OP for adding additional information.
So with regard to that information, if you are using a Dynamic Web Project, you may be able to add a container specific deployment descriptor to WebContent directory.
For tomcat, META-INF/context.xml
For Jetty, WEB-INF/jetty-web.xml
With those, you can define a JNDI data source. Refer to the specific container for information on how to do that. Once done, Hibernate and Quartz can be configured to use a JNDI reference for the data source you have configured.

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

EJB calls EJB in WebLogic and OpenEJB

I want to write an application which has 2 EJBs. This application can run in both OpenEJB and WebLogic 10.3. Both of the EJB are EJB 3.0.
I know how to implement in both OpenEJB and WebLogic, but the problem is I want to use the same code to deploy to both environments. I think the problem is that how to do JNDI lookup, because WebLogic's Context.INITIAL_CONTEXT_FACTORY is weblogic.jndi.WLInitialContextFactory but OpenEJB is not.
Current idea is the 1st EJB use a service locator to lookup the 2nd EJB and the service locator will read different INI in 2 environments. Is there any other suggestion? Is there a solution I can just use annotation, no need to use external INI files.
The 2 EJBs live in one container, but it's possible one will be move to other container in the future.
Update on 2011/10/06
By David's suggestion, I put some change. The code is a POJO, not JUnit code. It doesn't use #LocalClient and initialContext.bind("inject", this); (I put the 2 code in my JUnit code)
Put resources\META-INF\application-client.xml (only contain )
Put resources\jndi.properties
jdbc/OrderDB = new://Resource?type=DataSource
jdbc/OrderDB.JdbcDriver = oracle.jdbc.OracleDriver
jdbc/OrderDB.JdbcUrl = jdbc:oracle:thin:#*.*.*.*:1521:test
jdbc/OrderDB.JtaManaged = false
jdbc/OrderDB.UserName = test
jdbc/OrderDB.Password = test
Lookup code
InitialContext ctx= new InitialContext();
ctx.lookup("jdbc/" + name);
The following is the log, OpenEJB creates the JNDI for the database. I also use Eclipse debug mode to see the content of "ctx" and find "jdbc/OrderDB" in MyProps
INFO - Configuring Service(id=jdbc/OrderDB, type=Resource, provider-id=Default JDBC Database)
But finally I still cannot lookup it. I also try to use ctx.lookup(name), ctx.lookup("java:comp/env/jdbc/" + name) and the result is the same.
javax.naming.NameNotFoundException: Name "jdbc/OrderDB" not found.
Update on 2011/10/12
Base on David's comment, before Java EE6, I think the only solution is to use a service locator and some configuration to use different JNDI between WebLogic and OpenEJB. The following is the test result.
DB: WebLogic: OrderDB, OpenEJB: openejb:Resource/jdbc/OrderDB
Transaction manager: WebLogic: javax.transaction.TransactionManager, OpenEJB: java:comp/TransactionManager
EJB: Both of them just lookup the EJB name without any prefix
The question in the update is a very different question, so posting a different answer.
No Global JNDI prior to Java EE 6
The long and short of it is that prior to Java EE 6, there is no global JNDI. So it is quite literally the case that the question "what is the JNDI name of x" is an unanswerable question. Each EJB has its own private JNDI namespace and "POJOs" don't have any namespace at all, they use the JNDI namespace of whatever EJB invoked it. So to make "java:comp/env/myDataSource" appear as global as possible, you have to declare that reference for every single EJB in the application.
The amount of configuration work this creates for users is quite devastating. In Java EE 6 there is finally Global JNDI and three new standard namespaces, java:module, java:app and java:global. Any Global JNDI functionality existing prior to Java EE 6 is vendor-specific and non-portable.
The vendor-specific and non-portable way to do a Global JNDI lookup in OpenEJB for the given name would be to lookup openejb:Resource/jdbc/OrderDB
Calling a spade a spade
In OpenEJB we deliberately do not support non-standard lookups like jdbc/OrderDB or java:jdbc/OrderDB as some vendors do. The required prefix for global names in OpenEJB is openejb:.
JNDI is complex and confusing enough and making non-portable names look like portable names doesn't do users any favors. If a certain style of naming is not portable and going to create vendor lock-in, it should look like it. So with the openejb: prefix, you can access anything you need globally but it is at least clear that what you are doing is not portable and should not be expected to work in other platforms without some modification.
Note that there is a standard jndi.properties file you can use to externalize 100% of the config normally passed in as properties to the IntitialContext
You can still use a service locator pattern as it can make your code look a little nicer and perhaps easier to maintain, but the actual server connection information can be easily externalized.
You just need to make sure the jndi.properties file is on the client's classpath at the root (i.e. not in a META-INF directory). The IntialContext will find it and load it. Any properties passed into the IntialContext constructor will simply override those passed in via jndi.properties
On the OpenEJB side it should be possible to change the JNDI name format so that it matches the WebLogic format. If not, let me know and we can add any missing meta-data to the formatter so that it is possible to match it exactly.
Can't you just use the default context? Then you don't have to specify the specific implementation and you can do the lookup via a standard reference.
Otherwise I think you are left with some sort of properties file to determine the context details at runtime.

Best practice? JNDI, Hibernate and Tomcat

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.

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