4

I would like to use Spring as a JNDI provider. This means that I would like to configure a bean in my Spring context, which can be accessed via JNDI. This would look something like this:

<bean class="org.some.thing.here">
    <property name="beans">
        <map>
            <entry key="w/t/f">
                <bean class="some.thing.Else">
                     // rest ommitted
                </bean>
            </entry>
        </map>
    </property>
</bean>

Then, in my application (lets say a Controller), I want to be able to grab this bean via:

Context ctx = new InitialContext();
some.thing.Else bar = (some.thing.Else) ctx.lookup("w/t/f");

How could I go about doing this? I've looked at XBean, however the project looks out of date (doesn't work with Spring 3.0.X I don't think), and there is very little documentation.

Any other options? I would also considering rolling my own jndi provider class if it isn't too hard to do.

EDIT: I should add that I don't have an option using JNDI, I have a library we have to use which requires certain components to be loaded via JNDI. I would like to use Spring as the provider.

DigitalZebra
  • 39,494
  • 39
  • 114
  • 146

3 Answers3

5

Why use JNDI at all? Just get the Spring ApplicationContext and get the bean from that.

Assuming you initialized Spring using ContextLoaderListener in your webapp, you should be able to retrieve the application context from the ServletContext. From there you can get any bean you declared in Spring.

ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object bean = context.getBean(some.thing.Else.class);

If you have to use JDNI, then you can create a ServletContextListener that does something like the following in contextInitialized():

ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);

Object bean = context.getBean(some.thing.Else.class);

Context initCtx = new InitialContext();
Context springCtx = initCtx.createSubcontext("spring");

springCtx.bind("bean", bean);

Then, you should be able to lookup the Spring bean at "spring/bean" from the InitialContext.

Two things to note:

  1. The context listener should probably also call initCtx.destroySubcontext("spring") in contextDestroy too.

  2. The java:comp/env namespace is read-only (in Tomcat at least), so you can't put anything there.


Asker edit: Just a couple more points of clarity...

If you plan on referencing Spring beans via ApplicationContext, then you need a ContextLoaderListener defined in your web.xml. This must be defined before your custom listener class... like so:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
    <listener-class>
    org.example.sandbox.MyCustomServletContextListener
  </listener-class>
</listener> 

Also, you can get the ServletContext that getWebApplicationContext uses from the ServletContextEvent, like so:

@Override
public void contextInitialized(ServletContextEvent contextEvent) {
    try {
        ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(contextEvent.getServletContext());

        // get a bean named "myCalendar" from the application context       
        Calendar cal = (Calendar)appContext.getBean("myCalendar");

        // bind via JNDI
        Context initialContext = new InitialContext();
        Context subCtx = initialContext.createSubcontext("sample");
        subCtx.bind("calendar", cal);

    } catch (NamingException e) { // ommitted }
}
DigitalZebra
  • 39,494
  • 39
  • 114
  • 146
AngerClown
  • 6,149
  • 1
  • 25
  • 28
  • 1
    I'm forced to use JNDI, I don't have an option. I'm using a library which is looking for various components to be loaded via JNDI. – DigitalZebra Dec 11 '10 at 01:48
  • 1
    Can you programatically add beans to the JNDI initialContext? If so, you could write a ServletContextListener that gets the spring app context, pulls the beans from that then puts them into JNDI. – AngerClown Dec 11 '10 at 13:51
  • AngerClown, that is probably what I'm looking to do, if you have any suggestions on how to do that I'd be very thankful. – DigitalZebra Dec 11 '10 at 16:40
  • AngerClown, this looks like it works really well, and doesn't really feel hacky at all :). I have a simple proof of concept running, I will try it out on Tuesday when I get back to work. I added some additional notes to your answer to help anyone else who might stumble across this :). – DigitalZebra Dec 11 '10 at 22:09
  • 1
    These edits are a good start, but how are you going to write a JUnit test for this? You really want to use the lifecycle interfaces that are provided by Spring, not call getWebAppicationContext() or getBean(). – Brian Topping Dec 12 '10 at 21:38
  • Brian, thank you for the input. If you wouldn't mind, could you add a little bit of example code to your answer to show what you mean by implementing the lifecycle interfaces? Forgive me, I'm a bit new to Spring :( – DigitalZebra Dec 13 '10 at 01:57
2

AngerClown is right, don't bother with JNDI unless you need to provide references to other modules that insist on it. If you are in a webapp container like Tomcat, it will have a JNDI registry. Use that. If not inside a webapp container, it doesn't make sense to have JNDI anyway, since it's for J2EE environments.

Assuming you are inside a webapp, a better way to launch your app is to have the main class be a Spring bean that implements lifecycle interfaces (like InitializingBean) to get a call when it's time to start your app. By that point, your main application class will have been injected with all it's dependencies. This avoids the need to call methods on the ApplicationContext directly.

Even so, if you must call methods on the ApplicationContext and you are launched by Spring, you can implement BeanContextAware and get injected with the context.

Brian Topping
  • 3,235
  • 28
  • 33
  • Oh, I see the edits now for the JNDI need. But doesn't your container provide it? – Brian Topping Dec 11 '10 at 02:04
  • Yes, but I'm having tons of issues with Tomcat's container, and there is very little documentation... I figured I might be able to work with something in Spring instead (that would have more information). – DigitalZebra Dec 11 '10 at 02:13
1

Yet another way to write your own JndiExporter

https://blog.konstantinpavlov.net/2008/12/31/how-to-export-spring-bean-to-jndi/

Konstantin Pavlov
  • 956
  • 1
  • 10
  • 24