how to put(bind) object to jndi in spring declaratively?

落爺英雄遲暮 提交于 2019-11-27 23:06:49

Your can create a JndiExporter which uses a JndiTemplate to bind a map of object with a 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 have to implements BeanFactoryAware to retrieve the spring bean with the injected BeanFactory.

This is one possible may :)

Thanks for the questions. I wrote a variant of Treydone's solution and thought it might be useful to have actual code here (as it's 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 a configuration (with references) like this:

<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>

I realize this is an old question, but there's a way to do this without custom code. It's fairly verbose, but 100% declarative.

<!-- inside container, use JndiTemplate -->
<bean id="jndiBinder" class="org.springframework.jndi.JndiTemplate"/>
<!-- outside container (e.g. 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>

The MethodInvokingFactoryBean can also be used to set System properties (which comes in handy when using Atomikos), as long as the bean that reads the System properties depends-on that 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"/>

Hi There is no standard or best practices type approach for this problem. You will have come with with your own approach. Following is the another approach which can take care of your problem.

  1. Make javax.naming.InitialContext a spring bean (say initialContext). Make sure you pass it a appropriate map of initial properties as required.

  2. Now create another bean say JndiBinder. Inject the bean mentioned #1 above in this bean. This bean will take a map of jndi-names and corresponding objects. For your case, the object will be datasource, already available in spring context.

  3. In the JndiBinder bean definition, write a init-method which would call bind menthod of initialContext for all the entries in the map (of jndi-names and corresponding objects). This way all the entries in the map supplied are bound to JNDI tree.

If the code is being executed outside a Servlet container, e.g. in a unit test, the JNDI context needs to be emulated. Otherwise you'll get the dreaded "Need to specify class name in environment ..." error.

SimpleNamingContextBuilder is better suited for that, 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;
}

Do not overlook the "contextBuilder.activate();" line.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!