How to associate an object with jndi in spring declaratively?

We have a regular standalone spring application, and we need to put the jdbc datasource in jndi. (we use trekache jboss and it needs a data source for jndi).

Some googling searches found the most examples of jndi-lookup with spring, where the object is already placed in jndi (using tomcat or application server, etc.), but we need another way: I have a simple spring bean data source that I am adding to other services, but I can’t insert it in TreeCache because it is needed only from jndi.

Found org.springframework.jndi.JndiTemplate , which can be declared as a bean, for example:

 <bean id="fsJndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment"> <props> <prop key="java.naming.factory.initial">com.sun.jndi.fscontext.RefFSContextFactory</prop> <prop key="java.naming.provider.url">file:///c:\windows\temp</prop> </props> </property> </bean> 

but did not find how to associate with it, except in the java code: fsJndiTemplate.bind(name, obj) from the init method of another bean. Is there any way to do this declaratively?

+8
java spring configuration jndi
source share
5 answers

You can create a JndiExporter that uses a JndiTemplate to map an object map with the name:

 <bean id="jndiExporter" class="org.xxx.JndiExporter"> <property name="jndiTemplate" ref="jndiTemplate"> <property name="objects"> <map> <entry key="name1" value="bean1"/> <entry key="name2" value="bean2"/> <entry key="name3" value="bean3"/> <entry key="name4" value="bean4"/> </map> </property> </bean> 

Your JndiExporter must implement BeanFactoryAware to retrieve the spring bean using the entered BeanFactory.

This is one of the possible options :)

+7
source share

Thanks for the questions. I wrote a Treydone solution and thought it might be useful to have real code here (as it is pretty short):

 public class JndiExporter implements InitializingBean { private final JndiTemplate template = new JndiTemplate(); private Map<String, Object> jndiMapping = null; @Override public void afterPropertiesSet() throws Exception { for(Entry<String, Object> addToJndi: jndiMapping.entrySet()){ template.bind(addToJndi.getKey(), addToJndi.getValue()); } } public void setJndiMapping(Map<String, Object> jndiMapping) { this.jndiMapping = jndiMapping; } } 

Note that I implemented InitializingBean instead of BeanFactoryAware. This allows you to use the configuration (with links):

 <bean id="jndiExporter" class="com.ra.web.util.JndiExporter"> <property name="jndiMapping"> <map> <entry key="bean1" value-ref="other_spring_bean_id" /> <entry key="bean2" value="literal_value" /> </map> </property> </bean> 
+12
source share

I understand this is an old question, but there is a way to do this without special code. This is pretty verbose, but 100% declarative.

 <!-- inside container, use JndiTemplate --> <bean id="jndiBinder" class="org.springframework.jndi.JndiTemplate"/> <!-- outside container (eg for tests), use SimpleNamingContextBuilder --> <!-- <bean id="jndiBinder" class="org.springframework.mock.jndi.SimpleNamingContextBuilder" factory-method="emptyActivatedContextBuilder"/> --> <!-- use MethodInvokingFactoryBean to call 'bind' on 'jndiBinder' --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="jndiBinder"/> <property name="targetMethod" value="bind"/> <property name="arguments"> <array> <value type="java.lang.String">java:comp/UserTransaction</value> <ref bean="atomikosUserTransaction"/> </array> </property> </bean> <!-- define as many bindings as you need --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="jndiBinder"/> <property name="targetMethod" value="bind"/> <property name="arguments"> <array> <value type="java.lang.String">another/jndi/name</value> <value>literal_value</value> </array> </property> </bean> 

MethodInvokingFactoryBean can also be used to set system properties ( which comes in handy when using Atomikos ), while a bean that reads System depends-on properties is what MethodInvokingFactoryBean .

 <bean id="atomikosSystemProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject"> <bean class="java.lang.System" factory-method="getProperties"/> </property> <property name="targetMethod" value="putAll"/> <property name="arguments" ref="atomikosJtaProps"/> </bean> <bean id="atomikosJtaProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="com.atomikos.icatch.no_file">true</prop> <prop key="com.atomikos.icatch.hide_init_file_path">true</prop> <prop key="com.atomikos.icatch.service">com.atomikos.icatch.standalone.UserTransactionServiceFactory</prop> <prop key="com.atomikos.icatch.log_base_dir">/opt/txlogs</prop> </props> </property> </bean> <bean id="atomikosUserTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp" init-method="init" destroy-method="shutdownForce" depends-on="atomikosSystemProps"/> 
+5
source share

Hi There is no standard approach or best practice for this issue. You will come with your own approach. Below is another approach that might take care of your problem.

  • Make javax.naming.InitialContext a spring bean (say initialContext). Make sure you provide the appropriate source property map as needed.

  • Now create another bean, say, JndiBinder. Add the bean number 1 above to this bean. This bean will accept a map of jndi names and related objects. For your case, the object will be the data source already available in the spring context.

  • In the JndiBinder bean definition, write an init method that will call the bind menthod from initialContext for all the records on the map (from jndi names and corresponding objects). Thus, all entries in the attached map are tied to the JNDI tree.

+2
source share

If the code is executed outside the Servlet container, for example, in unit test, the JNDI context needs to be emulated. Otherwise, you get the terrible error "You must specify the class name in the environment ...".

SimpleNamingContextBuilder is better for this than JndiTemplate:

 public class JndiExporter implements InitializingBean { private final SimpleNamingContextBuilder contextBuilder = new SimpleNamingContextBuilder(); private Map<String, Object> jndiMapping = null; @Override public void afterPropertiesSet() throws Exception { for (Entry<String, Object> addToJndi : jndiMapping.entrySet()) { contextBuilder.bind(addToJndi.getKey(), addToJndi.getValue()); } contextBuilder.activate(); } public void setJndiMapping(Map<String, Object> jndiMapping) { this.jndiMapping = jndiMapping; } 

Watch out for "contextBuilder.activate ();" line.

0
source share

All Articles