001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.common.naming;
017
018import java.security.AccessController;
019import java.security.PrivilegedAction;
020import java.util.Hashtable;
021import javax.naming.Context;
022import javax.naming.spi.InitialContextFactory;
023import org.modeshape.common.SystemFailureException;
024
025/**
026 * An {@link InitialContextFactory} that provides a singleton {@link Context JNDI naming context}. Because it is a singleton, it
027 * is useful in unit tests that {@link Context#lookup(String) looks up} objects where it can be used within service registration
028 * logic while also being easily accessible in the test case itself.
029 * <p>
030 * For example, the following code sample shows how this InitialContextFactory implementation can be specified via the standard "
031 * <code>{@link Context#INITIAL_CONTEXT_FACTORY java.naming.factory.initial}</code>" property:
032 * 
033 * <pre>
034 * Hashtable&lt;String, Object&gt; jndiContext = new Hashtable&lt;String, Object&gt;();
035 * jndiContext.put(&quot;java.naming.factory.initial&quot;, &quot;org.modeshape.common.naming.SingleonInitialContextFactory&quot;);
036 * jndiContext.put(&quot;java.naming.provider.url&quot;, &quot;localhost&quot;);
037 * InitialContext initialContext = new InitialContext(jndiContext);
038 * </pre>
039 * 
040 * while the following sample shows how the same {@link Context} instance will be subsequently returned from accessed within other
041 * code (e.g., a test case):
042 * 
043 * <pre>
044 * Context context = SingletoneInitialContextFactory.create();
045 * </pre>
046 * 
047 * </p>
048 * 
049 * @author Luca Stancapiano
050 * @author Randall Hauch
051 */
052public class SingletonInitialContextFactory implements InitialContextFactory {
053
054    private static SingletonInitialContext SINGLETON;
055
056    public SingletonInitialContextFactory() {
057    }
058
059    @Override
060    public Context getInitialContext( Hashtable<?, ?> environment ) {
061        return getInstance(environment);
062    }
063
064    /**
065     * Return the {@link Context} singleton instance. If no such context has been configured, this method will configured the
066     * singletone using the supplied environment.
067     * 
068     * @param environment the environment for the naming context; may be null or empty
069     * @return the singleton context; never null
070     * @see #getInitialContext(Hashtable)
071     * @see #getInstance(Hashtable)
072     */
073    public static synchronized Context getInstance( Hashtable<?, ?> environment ) {
074        if (SINGLETON == null) SINGLETON = new SingletonInitialContext(environment);
075        return SINGLETON;
076    }
077
078    /**
079     * Return the previously-configured {@link Context} singleton instance. If no such context has been configured, this method
080     * throws a {@link SystemFailureException}.
081     * 
082     * @return the singleton context; never null
083     * @throws SystemFailureException if the singleton context has not yet been configured.
084     * @see #getInitialContext(Hashtable)
085     * @see #getInstance(Hashtable)
086     */
087    public static synchronized Context getInstance() {
088        if (SINGLETON == null) {
089            throw new SystemFailureException();
090        }
091        return SINGLETON;
092    }
093
094    /**
095     * Set the {@link Context#INITIAL_CONTEXT_FACTORY} system property to the name of this context's
096     * {@link SingletonInitialContextFactory factory class}.
097     */
098    public static void initialize() {
099        AccessController.doPrivileged(new PrivilegedAction<Void>() {
100            @Override
101            public Void run() {
102                System.setProperty("java.naming.factory.initial", SingletonInitialContextFactory.class.getName());
103                return null;
104            }
105        });
106
107    }
108
109    /**
110     * Release any existing singleton {@link Context naming context}. Any subsequent calls to {@link #getInstance(Hashtable)} or
111     * {@link #getInitialContext(Hashtable)} will return a new singleton instance.
112     */
113    public static synchronized void tearDown() {
114        SINGLETON = null;
115    }
116}