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.util.Hashtable;
019import java.util.Map;
020import java.util.concurrent.ConcurrentHashMap;
021import javax.naming.Binding;
022import javax.naming.Context;
023import javax.naming.Name;
024import javax.naming.NameAlreadyBoundException;
025import javax.naming.NameClassPair;
026import javax.naming.NameNotFoundException;
027import javax.naming.NameParser;
028import javax.naming.NamingEnumeration;
029import javax.naming.NamingException;
030import javax.naming.spi.InitialContextFactory;
031import org.modeshape.common.SystemFailureException;
032
033/**
034 * A simple and limited {@link Context JNDI naming context} that can be used in unit tests for code that
035 * {@link Context#lookup(String) looks up} objects.
036 * <p>
037 * This can be used easily in a unit test by either using one of two methods. The first is using the convenient static
038 * <code>configure</code> method that takes one, two or three name/object pairs:
039 * 
040 * <pre>
041 * SingletonInitialContext.register(name, obj);
042 * SingletonInitialContext.register(name1, obj1, name2, obj2);
043 * SingletonInitialContext.register(name1, obj1, name2, obj2, name3, obj3);
044 * </pre>
045 * 
046 * </p>
047 * <p>
048 * The other approach is to set the system property for the {@link InitialContextFactory}:
049 * 
050 * <pre>
051 * System.setProperty(&quot;java.naming.factory.initial&quot;, &quot;org.modeshape.common.mock.SingletonInitialContextFactory&quot;);
052 * </pre>
053 * 
054 * and then to {@link Context#bind(String, Object) bind} an object.
055 * 
056 * @see SingletonInitialContextFactory
057 * @author Luca Stancapiano
058 * @author Randall Hauch
059 */
060public class SingletonInitialContext implements Context {
061
062    /**
063     * A convenience method that registers the supplied object with the supplied name.
064     * 
065     * @param name the JNDI name
066     * @param obj the object to be registered
067     */
068    public static void register( String name,
069                                 Object obj ) {
070        register(name, obj, null, null, null, null);
071    }
072
073    /**
074     * A convenience method that registers the supplied objects with the supplied names.
075     * 
076     * @param name1 the JNDI name for the first object
077     * @param obj1 the first object to be registered
078     * @param name2 the JNDI name for the second object
079     * @param obj2 the second object to be registered
080     */
081    public static void register( String name1,
082                                 Object obj1,
083                                 String name2,
084                                 Object obj2 ) {
085        register(name1, obj1, name2, obj2, null, null);
086    }
087
088    /**
089     * A convenience method that registers the supplied objects with the supplied names.
090     * 
091     * @param name1 the JNDI name for the first object
092     * @param obj1 the first object to be registered
093     * @param name2 the JNDI name for the second object
094     * @param obj2 the second object to be registered
095     * @param name3 the JNDI name for the third object
096     * @param obj3 the third object to be registered
097     */
098    public static void register( String name1,
099                                 Object obj1,
100                                 String name2,
101                                 Object obj2,
102                                 String name3,
103                                 Object obj3 ) {
104        SingletonInitialContextFactory.initialize();
105        try {
106            javax.naming.InitialContext context = new javax.naming.InitialContext();
107            if (name1 != null) context.rebind(name1, obj1);
108            if (name2 != null) context.rebind(name2, obj2);
109            if (name3 != null) context.rebind(name3, obj3);
110        } catch (NamingException e) {
111            throw new SystemFailureException("Unable to create the mock InitialContext", e);
112        }
113    }
114
115    private final Map<String, Object> environment = new ConcurrentHashMap<String, Object>();
116    private final ConcurrentHashMap<String, Object> registry = new ConcurrentHashMap<String, Object>();
117
118    /* package */SingletonInitialContext( Hashtable<?, ?> environment ) {
119        if (environment != null) {
120            for (Map.Entry<?, ?> entry : environment.entrySet()) {
121                this.environment.put(entry.getKey().toString(), entry.getValue());
122            }
123        }
124    }
125
126    @Override
127    public Object addToEnvironment( String propName,
128                                    Object propVal ) {
129        return environment.put(propName, propVal);
130    }
131
132    @Override
133    public Object removeFromEnvironment( String propName ) {
134        return environment.remove(propName);
135    }
136
137    @Override
138    public void bind( Name name,
139                      Object obj ) throws NamingException {
140        bind(name.toString(), obj);
141    }
142
143    @Override
144    public void bind( String name,
145                      Object obj ) throws NamingException {
146        if (this.registry.putIfAbsent(name, obj) != null) {
147            throw new NameAlreadyBoundException("The name \"" + name + "\" is already bound to an object");
148        }
149    }
150
151    @Override
152    public void rebind( Name name,
153                        Object obj ) {
154        rebind(name.toString(), obj);
155    }
156
157    @Override
158    public void rebind( String name,
159                        Object obj ) {
160        this.registry.put(name, obj);
161    }
162
163    @Override
164    public void unbind( String name ) {
165        this.registry.remove(name);
166    }
167
168    @Override
169    public void unbind( Name name ) {
170        unbind(name.toString());
171    }
172
173    @Override
174    public Object lookup( Name name ) throws NamingException {
175        return lookup(name.toString());
176    }
177
178    @Override
179    public Object lookup( String name ) throws NamingException {
180        Object result = this.registry.get(name);
181        if (result == null) {
182            throw new NameNotFoundException("No object is registered at \"" + name + "\"");
183        }
184        return result;
185    }
186
187    @Override
188    public Object lookupLink( String name ) {
189        throw new UnsupportedOperationException();
190    }
191
192    @Override
193    public Object lookupLink( Name name ) {
194        throw new UnsupportedOperationException();
195    }
196
197    @Override
198    public void rename( Name oldName,
199                        Name newName ) {
200        throw new UnsupportedOperationException();
201    }
202
203    @Override
204    public void rename( String oldName,
205                        String newName ) {
206        throw new UnsupportedOperationException();
207    }
208
209    @Override
210    public void close() {
211    }
212
213    @Override
214    public Name composeName( Name name,
215                             Name prefix ) {
216        throw new UnsupportedOperationException();
217    }
218
219    @Override
220    public String composeName( String name,
221                               String prefix ) {
222        throw new UnsupportedOperationException();
223    }
224
225    @Override
226    public Context createSubcontext( Name name ) {
227        throw new UnsupportedOperationException();
228    }
229
230    @Override
231    public Context createSubcontext( String name ) {
232        throw new UnsupportedOperationException();
233    }
234
235    @Override
236    public void destroySubcontext( Name name ) {
237        throw new UnsupportedOperationException();
238    }
239
240    @Override
241    public void destroySubcontext( String name ) {
242        throw new UnsupportedOperationException();
243    }
244
245    @Override
246    public Hashtable<?, ?> getEnvironment() {
247        Hashtable<String, String> hashtable = new Hashtable<String, String>();
248        Map<?, ?> map = this.environment;
249        for (Map.Entry<?, ?> dd : map.entrySet()) {
250            hashtable.put(dd.getKey().toString(), dd.getValue().toString());
251        }
252        return hashtable;
253    }
254
255    @Override
256    public String getNameInNamespace() {
257        throw new UnsupportedOperationException();
258    }
259
260    @Override
261    public NameParser getNameParser( Name name ) {
262        throw new UnsupportedOperationException();
263    }
264
265    @Override
266    public NameParser getNameParser( String name ) {
267        throw new UnsupportedOperationException();
268    }
269
270    @Override
271    public NamingEnumeration<NameClassPair> list( Name name ) {
272        throw new UnsupportedOperationException();
273    }
274
275    @Override
276    public NamingEnumeration<NameClassPair> list( String name ) {
277        throw new UnsupportedOperationException();
278    }
279
280    @Override
281    public NamingEnumeration<Binding> listBindings( Name name ) {
282        throw new UnsupportedOperationException();
283    }
284
285    @Override
286    public NamingEnumeration<Binding> listBindings( String name ) {
287        throw new UnsupportedOperationException();
288    }
289}