001/* InitialContext.java -- Initial naming context.
002   Copyright (C) 2000, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.naming;
040
041import java.applet.Applet;
042import java.io.IOException;
043import java.io.InputStream;
044import java.net.URL;
045import java.util.Enumeration;
046import java.util.HashSet;
047import java.util.Hashtable;
048import java.util.Properties;
049
050import javax.naming.spi.NamingManager;
051
052/**
053 * The starting context for performing naming operations. All naming operations
054 * are performed in the scope of some context. The initial context is the
055 * starting point for the name resolution.
056 */
057public class InitialContext implements Context
058{
059  /**
060   * Contains the default initial context. This value is returned by
061   * {@link NamingManager#getInitialContext}. It is set by this method
062   * when calling it first time. The subsequent calls return the value of
063   * this field.
064   */
065  protected Context defaultInitCtx;
066  
067  /**
068   * Indicates if the initial context was obtained by calling
069   * {@link NamingManager#getInitialContext}. 
070   */
071  protected boolean gotDefault = false;
072  
073  /**
074   * The environment, associated with this initial context.
075   */
076  protected Hashtable<Object,Object> myProps;
077  
078  /**
079   * The list of the properties, to that the second alternative value must
080   * be appended after the colon to the first possible value. Used in
081   * {@link #merge(Hashtable, Hashtable)}
082   */
083  static final HashSet<String> colon_list;
084  static
085    {
086      colon_list = new HashSet<String>();
087      colon_list.add(Context.OBJECT_FACTORIES);
088      colon_list.add(Context.URL_PKG_PREFIXES);
089      colon_list.add(Context.STATE_FACTORIES);
090    }  
091    
092   /**
093    * The properties that are searched in the agreed places in the
094    * {@link #init(Hashtable)} method.
095    */
096    static final String[] use_properties = 
097      {
098        Context.DNS_URL,
099        Context.INITIAL_CONTEXT_FACTORY,
100        Context.OBJECT_FACTORIES,
101        Context.PROVIDER_URL,
102        Context.STATE_FACTORIES,
103        Context.URL_PKG_PREFIXES,
104      };
105    
106  
107  /**
108   * Creates the new initial context with the given properties.
109   * 
110   * @param environment the properties, used by the initial context being
111   *          created.
112   * @throws NamingException
113   */
114  public InitialContext(Hashtable<?,?> environment) throws NamingException
115  {
116    init(environment);
117  }
118  
119  /**
120   * Creates the initial context with the possibility to delay its
121   * initialisation.
122   * 
123   * @param lazy specified if the initialization should not be performed by this
124   *          constructor (true). If the valueis false, it works the same way as
125   *          the parameterless constructor.
126   * @throws NamingException
127   */
128  protected InitialContext(boolean lazy) throws NamingException
129  {
130    if (! lazy)
131      init(null);
132  }
133  
134  /**
135   * Creates teh new initial context with no properties. Same as
136   * InitialContext(null).
137   * 
138   * @throws NamingException
139   */
140  public InitialContext() throws NamingException
141  {
142    init(null);
143  }
144 
145  /**
146   * <p>
147   * Initialises the context, using the properties, specified in the passed
148   * table.
149   * </p>
150   * The missing properties are additionally obtained (in order) from the
151   * following locations:
152   * <ul>
153   * <li>If the passed parameter contains the key Context.APPLET, its value
154   * must be the instance of the {@link Applet}. Then the properties are
155   * requested via {@link Applet#getParameter(String)}.</li>
156   * <li>The value of the system property is used.</li>
157   * <li>The resource "jndi.properties" is requested from the context class
158   * loader of the current thread</li>
159   * <li>The property file "jndi.properties" is read from the location,
160   * specified by the system property "gnu.classpath.home.url".
161   * </ul>
162   * </p>
163   * 
164   * @param environment the table of the properties, may be null. The method
165   *          modifies the table and stores the reference to it. The caller must
166   *          not later reuse this structure for other purposes.
167   * @since 1.3
168   */
169  protected void init(Hashtable<?, ?> environment) throws NamingException
170  {
171    // If is documented that the caller should not modify the environment.
172    if (environment != null)
173      myProps = (Hashtable<Object, Object>) environment;
174    else
175      myProps = new Hashtable<Object, Object>();
176
177    Applet napplet = (Applet) myProps.get(Context.APPLET);
178
179    Properties pApplet = null;
180    if (napplet != null)
181      pApplet = new Properties();
182    Properties pSystem = new Properties();
183    Object value;
184
185    for (int i = use_properties.length - 1; i >= 0; i--)
186      {
187        String key = use_properties[i];
188        if (napplet != null)
189          {
190            value = napplet.getParameter(key);
191            if (value != null)
192              pApplet.put(key, value);
193          }
194        
195        value = System.getProperty(key);
196        if (value != null)
197          pSystem.put(key, value);
198      }
199    
200    merge(myProps, pSystem);
201    if (pApplet != null)
202      merge(myProps, pApplet);
203
204    try
205      {
206        Enumeration ep = Thread.currentThread().
207          getContextClassLoader().getResources("jndi.properties");
208        while (ep.hasMoreElements())
209          {
210            URL url = (URL) ep.nextElement();
211            Properties p = new Properties();
212
213            try
214              {
215                InputStream is = url.openStream();
216                p.load(is);
217                is.close();
218              }
219            catch (IOException e)
220              {
221                // Ignore.
222              }
223
224            merge(myProps, p);
225          }
226      }
227    catch (IOException e)
228      {
229        // Ignore.
230      }
231
232    String home = System.getProperty("gnu.classpath.home.url");
233    if (home != null)
234      {
235        String url = home + "/jndi.properties";
236        Properties p = new Properties();
237
238        try
239          {
240            InputStream is = new URL(url).openStream();
241            p.load(is);
242            is.close();
243          }
244        catch (IOException e)
245          {
246            // Ignore.
247          }
248
249        merge(myProps, p);
250      }
251  }
252  
253  /**
254   * Merge the content of the two tables. If the second table contains the key
255   * that is missing in the first table, this key - value pair is copied to the
256   * first table. If both first and second tables contain the same key AND the
257   * {@link #colon_list} set also contains this key, the value from the second
258   * table is appended to the value from the first table after semicolon, and
259   * the resulted value replaces the value in the first table.
260   * 
261   * @param primary the first table to merge. The merged result is also stored
262   *          in this table.
263   * @param additional the second table, from where additional values are taken
264   */  
265  static void merge (Hashtable<Object, Object> primary,
266                     Hashtable<Object, Object> additional)
267  {
268    Enumeration en = additional.keys();
269    
270    while (en.hasMoreElements())
271      {
272        String key2 = (String) en.nextElement();
273        Object value1 = primary.get(key2);
274        if (value1 == null)
275          primary.put(key2, additional.get(key2));
276        else if (colon_list.contains(key2))
277          {
278            String value2 = (String) additional.get(key2);
279            primary.put(key2, (String) value1 + ":" + value2);
280          }
281      }
282  }
283  
284  /**
285   * Get the default initial context. If {@link #gotDefault} == false, this
286   * method obtains the initial context from the naming manager and sets
287   * gotDefault to true. Otherwise the cached value ({@link #defaultInitCtx} is
288   * returned.
289   * 
290   * @return the default initial context
291   * @throws NamingException
292   */
293  protected Context getDefaultInitCtx() throws NamingException
294  {
295    if (! gotDefault)
296      {
297        defaultInitCtx = NamingManager.getInitialContext(myProps);
298        gotDefault = true;
299      }
300    return defaultInitCtx;
301  }
302
303  /**
304   * Obtains the context for resolving the given name. If the first component of
305   * the name is the URL string, this method tries to find the corressponding
306   * URL naming context. If it is not an URL string, or the URL context is not
307   * found, the default initial context is returned.
308   * 
309   * @param name the name, for that it is required to obtain the context.
310   * @return the context for resolving the name.
311   * @throws NamingException
312   */
313  protected Context getURLOrDefaultInitCtx(Name name) throws NamingException
314  {
315    if (name.size() > 0)
316      return getURLOrDefaultInitCtx(name.get(0));
317    else
318      return getDefaultInitCtx();
319  }
320
321  /**
322   * Obtains the context for resolving the given name. If the first component of
323   * the name is the URL string, this method tries to find the corressponding
324   * URL naming context. If it is not an URL string, or the URL context is not
325   * found, the default initial context is returned.
326   * 
327   * @param name the name, for that it is required to obtain the context.
328   * @return the context for resolving the name.
329   * @throws NamingException
330   */
331  protected Context getURLOrDefaultInitCtx(String name) throws NamingException
332  {
333    String scheme = null;
334
335    if (NamingManager.hasInitialContextFactoryBuilder())
336      return getDefaultInitCtx();
337    int colon = name.indexOf(':');
338    int slash = name.indexOf('/');
339    if (colon > 0 && (slash == - 1 || colon < slash))
340      scheme = name.substring(0, colon);
341    if (scheme != null)
342      {
343        Context context = NamingManager.getURLContext(scheme, myProps);
344        if (context != null)
345          return context;
346      }
347
348    return getDefaultInitCtx();
349  }
350
351  /** @inheritDoc */  
352  public void bind (Name name, Object obj) throws NamingException
353  {
354    getURLOrDefaultInitCtx (name).bind (name, obj);
355  }
356
357  /** @inheritDoc */  
358  public void bind (String name, Object obj) throws NamingException
359  {
360    getURLOrDefaultInitCtx (name).bind (name, obj);
361  }
362
363  /** @inheritDoc */  
364  public Object lookup (Name name) throws NamingException
365  {
366    try
367      {
368        return getURLOrDefaultInitCtx (name).lookup (name);
369      }
370    catch (CannotProceedException cpe)
371      {
372        Context ctx = NamingManager.getContinuationContext (cpe);
373        return ctx.lookup (cpe.getRemainingName());
374      }
375  }
376
377  /** @inheritDoc */  
378  public Object lookup (String name) throws NamingException
379  {
380      try
381        {
382          return getURLOrDefaultInitCtx (name).lookup (name);
383        }
384      catch (CannotProceedException cpe)
385        {
386          Context ctx = NamingManager.getContinuationContext (cpe);
387          return ctx.lookup (cpe.getRemainingName());
388        }
389  }
390
391  /** @inheritDoc */  
392  public void rebind (Name name, Object obj) throws NamingException
393  {
394    getURLOrDefaultInitCtx (name).rebind (name, obj);
395  }
396  
397  /** @inheritDoc */
398  public void rebind (String name, Object obj) throws NamingException
399  {
400    getURLOrDefaultInitCtx (name).rebind (name, obj);
401  }
402
403  /** @inheritDoc */  
404  public void unbind (Name name) throws NamingException
405  {
406    getURLOrDefaultInitCtx (name).unbind (name);
407  }
408
409  /** @inheritDoc */  
410  public void unbind (String name) throws NamingException
411  {
412    getURLOrDefaultInitCtx (name).unbind (name);
413  }
414
415  /** @inheritDoc */  
416  public void rename (Name oldName, Name newName) throws NamingException
417  {
418    getURLOrDefaultInitCtx (oldName).rename (oldName, newName);
419  }
420
421  /** @inheritDoc */  
422  public void rename (String oldName, String newName) throws NamingException
423  {
424    getURLOrDefaultInitCtx (oldName).rename (oldName, newName);
425  }
426
427  /** @inheritDoc */  
428  public NamingEnumeration<NameClassPair> list (Name name) throws NamingException
429  {
430    return getURLOrDefaultInitCtx (name).list (name);
431  }
432
433  /** @inheritDoc */  
434  public NamingEnumeration<NameClassPair> list (String name) throws NamingException
435  {
436    return getURLOrDefaultInitCtx (name).list (name);
437  }
438
439  /** @inheritDoc */  
440  public NamingEnumeration<Binding> listBindings (Name name) throws NamingException
441  {
442    return getURLOrDefaultInitCtx (name).listBindings (name);
443  }
444
445  /** @inheritDoc */  
446  public NamingEnumeration<Binding> listBindings (String name) throws NamingException
447  {
448    return getURLOrDefaultInitCtx (name).listBindings (name);
449  }
450
451  /** @inheritDoc */  
452  public void destroySubcontext (Name name) throws NamingException
453  {
454    getURLOrDefaultInitCtx (name).destroySubcontext (name);
455  }
456
457  /** @inheritDoc */  
458  public void destroySubcontext (String name) throws NamingException
459  {
460    getURLOrDefaultInitCtx (name).destroySubcontext (name);
461  }
462
463  /** @inheritDoc */  
464  public Context createSubcontext (Name name) throws NamingException
465  {
466    return getURLOrDefaultInitCtx (name).createSubcontext (name);
467  }
468
469  /** @inheritDoc */  
470  public Context createSubcontext (String name) throws NamingException
471  {
472    return getURLOrDefaultInitCtx (name).createSubcontext (name);
473  }
474
475  /** @inheritDoc */  
476  public Object lookupLink (Name name) throws NamingException
477  {
478    return getURLOrDefaultInitCtx (name).lookupLink (name);
479  }
480
481  /** @inheritDoc */  
482  public Object lookupLink (String name) throws NamingException
483  {
484    return getURLOrDefaultInitCtx (name).lookupLink (name);
485  }
486
487  /** @inheritDoc */  
488  public NameParser getNameParser (Name name) throws NamingException
489  {
490    return getURLOrDefaultInitCtx (name).getNameParser (name);
491  }
492
493  /** @inheritDoc */  
494  public NameParser getNameParser (String name) throws NamingException
495  {
496    return getURLOrDefaultInitCtx (name).getNameParser (name);
497  }
498
499  /** @inheritDoc */  
500  public Name composeName (Name name, Name prefix) throws NamingException
501  {
502    return getURLOrDefaultInitCtx (name).composeName (name, prefix);
503  }
504
505  /** @inheritDoc */  
506  public String composeName (String name, 
507                             String prefix) throws NamingException
508  {
509    return getURLOrDefaultInitCtx (name).composeName (name, prefix);
510  }
511  
512  /** @inheritDoc */
513  public Object addToEnvironment (String propName, 
514                                  Object propVal) throws NamingException
515  {
516    return myProps.put (propName, propVal);
517  }
518
519  /** @inheritDoc */  
520  public Object removeFromEnvironment (String propName) throws NamingException
521  {
522    return myProps.remove (propName);
523  }
524
525  /** @inheritDoc */  
526  public Hashtable<?,?> getEnvironment () throws NamingException
527  {
528    return myProps;
529  }
530
531  /** @inheritDoc */  
532  public void close () throws NamingException
533  {
534    myProps = null;
535    defaultInitCtx = null;
536  }
537
538  /**
539   * This operation is not supported for the initial naming context.
540   * 
541   * @throws OperationNotSupportedException always, unless the method is
542   *           overridden in the derived class.
543   */
544  public String getNameInNamespace () throws NamingException
545  {
546    throw new OperationNotSupportedException ();
547  }
548}