Using JNDI with Emedded Tomcat

Using JNDI with Emedded Tomcat

Recently I was working on a project where we were migrating an application off of WebSphere and onto Spring Boot. This application used JNDI to store configuration in JNDI. While we were ultimately planning to extract this configuration using Spring Cloud Config Server I felt it worthwhile to udnerstand how Tomcat works with JNDI.

Spring Boot and Embedded Tomcat

Spring Boot by default uses Tomcat as its web container. During apaplication startup it initializes a pretty bare bones Tomcat instance. The framework does provide a few hook points in order to customize the configuartion.

If this were standard XML based configuration you would add your environment configuration to the context.xml as follows:

1<Context ...>
2  ...
3  <Environment name="maxExemptions" value="10"
4         type="java.lang.Integer" override="false"/>
5  ...
6</Context>

Since we are planning to do Spring Java configration we must make the following two changes in an @Configuration class.

The two changes we ned to make are.

  1. Enable Naming. Embedded Tomcat by default does not have naming enabled.
  2. Add Name vaule Pairs to the context.

Spring Boot provides an out of the box bean called a ServletWebServerFactory. We will be using this class to customize our application.

 1@Bean
 2public ServletWebServerFactory webServerFactory() {
 3	TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(){
 4		@Override
 5		protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
 6			//naming is not enabled by default
 7			tomcat.enableNaming();
 8			return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
 9		}
10	};
11	factory.addContextCustomizers(new TomcatContextCustomizer() {
12		@Override
13		public void customize(Context context) {
14			//add a Context Environment for each value.
15			ContextEnvironment ce = new ContextEnvironment();
16			ce.setName("maxExemptions");
17			ce.setValue("1234");
18			ce.setType("java.lang.String");
19			context.getNamingResources().addEnvironment(ce);
20			}
21		});
22		return factory;
23	}

We could grab our context values easily using @Value annotations that we inject via the standard Spring configuration mechanisms.

Reading the Config

Now that we have a new JNDI entry called foo we need some code to read that property.

The following code illustrates how to read the property in a Struts Action class.

 1public ActionForward execute(ActionMapping mapping,ActionForm form,
 2			HttpServletRequest request,HttpServletResponse response) throws Exception {
 3
 4		JndiTemplate jndiTemplate = new JndiTemplate();
 5
 6		String foo = (String)jndiTemplate.lookup("java:/comp/env/maxExemptions");
 7
 8		HelloWorldForm helloWorldForm = (HelloWorldForm) form;
 9		helloWorldForm.setMessage("Hello World! Struts: "+foo);
10		
11		return mapping.findForward("success");
12	}

One thing that was problematic for us is that Embedded Tomcat puts all naming resources into java:comp/env/ This meant we still had to make some changes to the original code running on WebSphere since it didn't assume this location.

Readig StackOverflow it seems it may be possible to customize this behavior by implementing our own InitialContextFactory but if we go down that route we would still need to change the library doing lookups so I'd rather just externalize the JNDI strings so they can be changed depending on if the application is running on Tomcat or WebSphere

Leave a comment below if you have any ideas on how to avoid needing to prepend lookup Strings with java:/comp/env

comments powered by Disqus