Which issue does JNDI solve that ApplicationContext does not solve in Spring? - java

JNDI is a mean to retrieve/store data or objects from string names. This feature is provided by the container running the application.
ApplicationContext allows the creation and retrieval of beans from their string name.
Both serve similar needs. Yet, Spring offers means to retrieve objects from JNDI. One can also access JNDI via the JndiTemplate.
Is there a real need to use JNDI in Spring? Which problem does it solve that ApplicationContext does not?

The Application Context would not help you in looking up a REMOTE object. It will only look for objects in the current application, which are not remote.
See the following "Context.PROVIDER_URL". You can get access to REMOTE objects like EJBs or RMI or JMS, etc. Also, you could access any resource managed by the Java EE Container such as a DataSource.
ht.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL,"t3://HOSTNAME:PORT");

It's a question of scope. Spring's scope is limited to your application inside its container. JNDI is a global naming API that can plug into many different naming/directory architectures.

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.

Life cycle/length of a DataSource injected with #Resource annotation

I'm far from an expert of the intricacies of Resource Injection, and of indeed DataSources in Java, but I generally understand the process of doing a lookup for a predefined JNDI resource to get a datasource from.
Using Resource Injection as an alternate method, the below syntax works:
#Resource(name="jdbc/Foo")
private javax.sql.DataSource con;
However, I am using this in a servlet and as such wondering, how long this injected connection object's value will exist? Presumably, as it's simply an object within the servlet, it will inject when the servlet is first instantiated and exist for the same duration as the servlet (assuming I don't manually change it). Is this correct? Or does the servlet re-inject the resource everytime the servlet is used?
Thanks
A servlet container only ever creates one instance of your servlet. The IoC container you're using will then instantiate and inject the DataSource, so the value in con will remain the same for the life of your servlet, ie. the life of the application.
As to the underlying connection the DataSource is trying to make will, that's up to your datasource.
it will inject when the servlet is first instantiated and exist for the same duration as the servlet
Correct.
Or does the servlet re-inject the resource everytime the servlet is used?
No, this couldn't be happening because each request is served by a different thread. It wouldn't be good if those threads would modify the fields of the servlet. Request processing methods of the servlet must not modify its fields.
Generally container managed resources are injected using #Resources annotation. And container managed resources live as long as the container is running ( unless your are not manually kill it or any exception happens). Several applications can use the same resource object, provided that they live in the same application server ecosystem (same application server or cluster or server domain). But servlets are managed by your applications and lives within the application's scope as long as your application is running! So in terms of life span, if you compare resources are longer living than servlets.
And yes you are right, if you inject resources to a servlet, the reference of the resource will remain from the creation of the servlet to the end of the servlet's life cycle. The injection is not related with, how you are using the servlet or the resource.
Hope, this answers your question, Thanks!

EJB 3.1 remove invocation context for security purpose (ThreadLocal, ...)

I have a webapp on one Glassfish server (front-end) and an EJB 3.1 app (back-end) on another Glassfish server. The webapp communicates with the EJB 3.1 via remote invocation.
I would like to pass context data (user data i.e.) without having to define it as an input parameter of each business operation.
I have one idea, but not sure it will work: use a ThreadLocal to store data, but the ThreadLocal will only be available on one server (meaning JVM) => use the InvocationContext object and create interceptor to add user data to the ContextData Map.
What do you think about it? Any other ideas are more than welcome! ;-)
UPDATE
After first answer, I googled it a little bit and found the annotation #CallerPrincipal.
How can I set this object before the remote invocation?
The container will already handle this so you don't have to code it yourself.
In your EJB, you can access the EJBContext, which has a getCallerPrincipal() method which will give you the callers identity.

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.

Packaging EJBs in Java EE server

I have deployed an enterprise app in an EAR and a standalone web app in WAR (out side of the EAR) to the same Java EE server.
Question is can I access the local interface of the session bean in the EAR from the standalone WAR? if so can I use dependency injection (DI)?
Section 3.2.2 of the EJB 3.1 specification explicitly states that this is not portable:
Access to an enterprise bean through
the local client view is only required
to be supported for local clients
packaged within the same application
as the enterprise bean that provides
the local client view. Compliant
implementations of this specification
may optionally support access to the
local client view of an enterprise
bean from a local client packaged in a
different application. The
configuration requirements for
inter-application access to the local
client view are vendor-specific and
are outside the scope of this
specification. Applications relying on
inter-application access to the local
client view are non-portable.
In general, the solution will require at a minimum:
Some mechanism for allowing both the EAR and WAR class loaders to have visibility to the same interface class.
Some mechanism for looking up a local interface from another application. For example, java:global.
The answer anyway to both your questions is yes.
Since both the ear and war are in the same JVM, a local interface can be used. To get an instance of the session bean, you would use the global JNDI name of said bean to do either a JNDI lookup or use with the mappedName property on the #EJB annotation. Those names are standardized via the following pattern:
java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-qualified-interface-name>]
Lookup would thus be if the name of your ear is my_app, your bean is MyBean.java and its local interface is com.foo.SomeBeanLocal:
InitialContext ctx = InitialContext();
SomeBeanLocal someBean = (SomeBeanLocal) ctx.lookup("java:global/my_ear/SomeBean/com.foo.SomeBeanLocal");
Injection would be:
public SomeManagedClass {
#EJB(mappedName="java:global/my_ear/SomeBean/com.foo.SomeBeanLocal")
SomeBeanLocal someBean;
}
There are two catches however:
Non-standard naming
Older Java EE implementations (e.g. JBoss AS 5.1, Glassfish 2, Websphere) used their own naming pattern. E.g. JBoss AS 5.x would use:
<app-name>/<bean-name>/local|global
E.g. with the same names as the previous example, the global JNDI name of that bean in JBoss AS 5.1 would be:
my_app/MyBean/local
As said, other old application servers might use other names.
Startup-order
Especially with injection you have to assure somehow that the application you want to inject from has been started before the application you want to inject into. I.e. in your case that the EAR has been started before the standalone WAR. There are no standard mechanisms for this. You might be just lucky that it happens to be in the right order for you. This order might be influenced by time-stamps, alphabetical order of application names, or something else entirely.
JBoss AS for example uses <depends> tags in many of its proprietary configuration files, which is perfectly suited for this although it can be hard to find out what exactly you need to depend on (the syntax can be very cryptic).

Categories