Sunday 28 January 2018

How To + SpringBoot + Embedded Tomcat + JNDI + URL + File Resources

If you are building your application in SpringBoot with Embedded Tomcat and looking for reading property files using JNDI, this post may help you.

Let say you have an environment property file which you want to keep out of your web package  and it should be referred/read in your application using JNDI URL resource. You can follow below steps to achieve same.

1. Create the property file which you want to read using JNDI URL resource.

EnvPropFile
mykey=myvalue

2. Create URLFactory class which should implement ObjectFactory interface.

URLFactory.java
package com.xxx.example;

import java.net.URL;
import java.util.Hashtable;
import javax.naming.*;
import javax.naming.spi.ObjectFactory;

public class URLFactory implements ObjectFactory {
    public Object getObjectInstance(Object obj, Name name,
       Context nameCtx, Hashtable environment) throws Exception {
       Reference ref = (Reference) obj;
       String urlString = (String) ref.get("file").getContent();
       return new URL(urlString);
     }
}


3. Add below code in your spring boot application class that should initialize TomcatEmbeddedServletContainerFactory bean. Here in this bean, under postProcessContext() method, you can define JNDI file resources as mentioned below.  Factory class should be same that you created in step #2.

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
            
  return new TomcatEmbeddedServletContainerFactory() {

     @Override
     protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                             Tomcat tomcat) {
         tomcat.enableNaming();
         return super.getTomcatEmbeddedServletContainer(tomcat);
     }

     @Override
     protected void postProcessContext(Context context) {
       ContextResource fileRes = new ContextResource();
       fileRes.setName("url/EnvPropFile");
       fileRes.setType(URL.class.getName());
       fileRes.setProperty("factory","com.xxx.example.URLFactory.java");
       fileRes.setProperty("file","file:///C:/your-path/EnvPropFile");
       context.getNamingResources().addResource(evolutionEngineConfigResource);                                         
      }
   };
}

4. Define the URL bean which should return URL using resource name lookup. Keep 'java:comp/env' as it is and add your resource name as mentioned above.


import java.net.MalformedURLException;
import java.net.URL;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;

@Configuration
public class ConfigFileConfiguration {

 @Bean(name="envPropFile", destroyMethod = "")
  public URL envPropFile() throws MalformedURLException, NamingException{
                              InitialContext context = new InitialContext();
                              return (URL) context.lookup("java:comp/env/url/EnvPropFile");
   }
}


5. Now autowire this URL bean in other spring component where you want to access the properties. Note that, in actual production code, file initialization should happen only once. 

@Component
public class MyService {
  
    @Autowired
    private URL envPropFile;

    public   void pringEnvPropFileKey() throws Exception{
    
      Properties configHolder = new Properties();
      
      final URLConnection conn = envPropFile.openConnection();
      final InputStream inputStream = conn.getInputStream();
      configHolder.load(inputStream);
   
      String value = configHolder.getProperty("mykey"); // This is the key mentioned in prop file
   
      System.out.println(value);

   }
}

I think that's pretty much what you need to do to read file using JNDI in spring boot embedded tomcat application.To get more detail specific to tomcat and it's JNDI resources you can refer this link.