Save This Page
Home » Spring-Framework-090522 » org.springframework.mock » jndi » [javadoc | source]
    1   /*
    2    * Copyright 2002-2009 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.mock.jndi;
   18   
   19   import java.util.Hashtable;
   20   
   21   import javax.naming.Context;
   22   import javax.naming.NamingException;
   23   import javax.naming.spi.InitialContextFactory;
   24   import javax.naming.spi.InitialContextFactoryBuilder;
   25   import javax.naming.spi.NamingManager;
   26   
   27   import org.apache.commons.logging.Log;
   28   import org.apache.commons.logging.LogFactory;
   29   
   30   import org.springframework.util.ClassUtils;
   31   
   32   /**
   33    * Simple implementation of a JNDI naming context builder.
   34    *
   35    * <p>Mainly targeted at test environments, where each test case can
   36    * configure JNDI appropriately, so that <code>new InitialContext()</code>
   37    * will expose the required objects. Also usable for standalone applications,
   38    * e.g. for binding a JDBC DataSource to a well-known JNDI location, to be
   39    * able to use traditional J2EE data access code outside of a J2EE container.
   40    *
   41    * <p>There are various choices for DataSource implementations:
   42    * <ul>
   43    * <li>SingleConnectionDataSource (using the same Connection for all getConnection calls);
   44    * <li>DriverManagerDataSource (creating a new Connection on each getConnection call);
   45    * <li>Apache's Jakarta Commons DBCP offers BasicDataSource (a real pool).
   46    * </ul>
   47    *
   48    * <p>Typical usage in bootstrap code:
   49    *
   50    * <pre class="code">
   51    * SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
   52    * DataSource ds = new DriverManagerDataSource(...);
   53    * builder.bind("java:comp/env/jdbc/myds", ds);
   54    * builder.activate();</pre>
   55    * 
   56    * Note that it's impossible to activate multiple builders within the same JVM,
   57    * due to JNDI restrictions. Thus to configure a fresh builder repeatedly, use
   58    * the following code to get a reference to either an already activated builder
   59    * or a newly activated one:
   60    *
   61    * <pre class="code">
   62    * SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
   63    * DataSource ds = new DriverManagerDataSource(...);
   64    * builder.bind("java:comp/env/jdbc/myds", ds);</pre>
   65    *
   66    * Note that you <i>should not</i> call <code>activate()</code> on a builder from
   67    * this factory method, as there will already be an activated one in any case.
   68    *
   69    * <p>An instance of this class is only necessary at setup time.
   70    * An application does not need to keep a reference to it after activation.
   71    *
   72    * @author Juergen Hoeller
   73    * @author Rod Johnson
   74    * @see #emptyActivatedContextBuilder()
   75    * @see #bind(String, Object)
   76    * @see #activate()
   77    * @see org.springframework.mock.jndi.SimpleNamingContext
   78    * @see org.springframework.jdbc.datasource.SingleConnectionDataSource
   79    * @see org.springframework.jdbc.datasource.DriverManagerDataSource
   80    * @see org.apache.commons.dbcp.BasicDataSource
   81    */
   82   public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder {
   83   
   84   	/** An instance of this class bound to JNDI */
   85   	private static volatile SimpleNamingContextBuilder activated;
   86   
   87   	private static boolean initialized = false;
   88   
   89   	private static final Object initializationLock = new Object();
   90   
   91   
   92   	/**
   93   	 * Checks if a SimpleNamingContextBuilder is active.
   94   	 * @return the current SimpleNamingContextBuilder instance,
   95   	 * or <code>null</code> if none
   96   	 */
   97   	public static SimpleNamingContextBuilder getCurrentContextBuilder() {
   98   		return activated;
   99   	}
  100   
  101   	/**
  102   	 * If no SimpleNamingContextBuilder is already configuring JNDI,
  103   	 * create and activate one. Otherwise take the existing activate
  104   	 * SimpleNamingContextBuilder, clear it and return it.
  105   	 * <p>This is mainly intended for test suites that want to
  106   	 * reinitialize JNDI bindings from scratch repeatedly.
  107   	 * @return an empty SimpleNamingContextBuilder that can be used
  108   	 * to control JNDI bindings
  109   	 */
  110   	public static SimpleNamingContextBuilder emptyActivatedContextBuilder() throws NamingException {
  111   		if (activated != null) {
  112   			// Clear already activated context builder.
  113   			activated.clear();
  114   		}
  115   		else {
  116   			// Create and activate new context builder.
  117   			SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
  118   			// The activate() call will cause an assignment to the activated variable.
  119   			builder.activate();
  120   		}
  121   		return activated;
  122   	}
  123   
  124   
  125   	private final Log logger = LogFactory.getLog(getClass());
  126   
  127   	private final Hashtable<String,Object> boundObjects = new Hashtable<String,Object>();
  128   
  129   
  130   	/**
  131   	 * Register the context builder by registering it with the JNDI NamingManager.
  132   	 * Note that once this has been done, <code>new InitialContext()</code> will always
  133   	 * return a context from this factory. Use the <code>emptyActivatedContextBuilder()</code>
  134   	 * static method to get an empty context (for example, in test methods).
  135   	 * @throws IllegalStateException if there's already a naming context builder
  136   	 * registered with the JNDI NamingManager
  137   	 */
  138   	public void activate() throws IllegalStateException, NamingException {
  139   		logger.info("Activating simple JNDI environment");
  140   		synchronized (initializationLock) {
  141   			if (!initialized) {
  142   				if (NamingManager.hasInitialContextFactoryBuilder()) {
  143   					throw new IllegalStateException(
  144   							"Cannot activate SimpleNamingContextBuilder: there is already a JNDI provider registered. " +
  145   							"Note that JNDI is a JVM-wide service, shared at the JVM system class loader level, " +
  146   							"with no reset option. As a consequence, a JNDI provider must only be registered once per JVM.");
  147   				}
  148   				NamingManager.setInitialContextFactoryBuilder(this);
  149   				initialized = true;
  150   			}
  151   		}
  152   		activated = this;
  153   	}
  154   
  155   	/**
  156   	 * Temporarily deactivate this context builder. It will remain registered with
  157   	 * the JNDI NamingManager but will delegate to the standard JNDI InitialContextFactory
  158   	 * (if configured) instead of exposing its own bound objects.
  159   	 * <p>Call <code>activate()</code> again in order to expose this context builder's own
  160   	 * bound objects again. Such activate/deactivate sequences can be applied any number
  161   	 * of times (e.g. within a larger integration test suite running in the same VM).
  162   	 * @see #activate()
  163   	 */
  164   	public void deactivate() {
  165   		logger.info("Deactivating simple JNDI environment");
  166   		activated = null;
  167   	}
  168   
  169   	/**
  170   	 * Clear all bindings in this context builder, while keeping it active.
  171   	 */
  172   	public void clear() {
  173   		this.boundObjects.clear();
  174   	}
  175   
  176   	/**
  177   	 * Bind the given object under the given name, for all naming contexts
  178   	 * that this context builder will generate.
  179   	 * @param name the JNDI name of the object (e.g. "java:comp/env/jdbc/myds")
  180   	 * @param obj the object to bind (e.g. a DataSource implementation)
  181   	 */
  182   	public void bind(String name, Object obj) {
  183   		if (logger.isInfoEnabled()) {
  184   			logger.info("Static JNDI binding: [" + name + "] = [" + obj + "]");
  185   		}
  186   		this.boundObjects.put(name, obj);
  187   	}
  188   
  189   
  190   	/**
  191   	 * Simple InitialContextFactoryBuilder implementation,
  192   	 * creating a new SimpleNamingContext instance.
  193   	 * @see SimpleNamingContext
  194   	 */
  195   	public InitialContextFactory createInitialContextFactory(Hashtable<?,?> environment) {
  196   		if (activated == null && environment != null) {
  197   			Object icf = environment.get(Context.INITIAL_CONTEXT_FACTORY);
  198   			if (icf != null) {
  199   				Class<?> icfClass = null;
  200   				if (icf instanceof Class) {
  201   					icfClass = (Class<?>) icf;
  202   				}
  203   				else if (icf instanceof String) {
  204   					icfClass = ClassUtils.resolveClassName((String) icf, getClass().getClassLoader());
  205   				}
  206   				else {
  207   					throw new IllegalArgumentException("Invalid value type for environment key [" +
  208   							Context.INITIAL_CONTEXT_FACTORY + "]: " + icf.getClass().getName());
  209   				}
  210   				if (!InitialContextFactory.class.isAssignableFrom(icfClass)) {
  211   					throw new IllegalArgumentException(
  212   							"Specified class does not implement [" + InitialContextFactory.class.getName() + "]: " + icf);
  213   				}
  214   				try {
  215   					return (InitialContextFactory) icfClass.newInstance();
  216   				}
  217   				catch (Throwable ex) {
  218   					IllegalStateException ise =
  219   							new IllegalStateException("Cannot instantiate specified InitialContextFactory: " + icf);
  220   					ise.initCause(ex);
  221   					throw ise;
  222   				}
  223   			}
  224   		}
  225   
  226   		// Default case...
  227   		return new InitialContextFactory() {
  228   			@SuppressWarnings("unchecked")
  229   			public Context getInitialContext(Hashtable<?,?> environment) {
  230   				return new SimpleNamingContext("", boundObjects, (Hashtable<String, Object>) environment);
  231   			}
  232   		};
  233   	}
  234   
  235   }

Save This Page
Home » Spring-Framework-090522 » org.springframework.mock » jndi » [javadoc | source]