001    /* InitialContext.java -- Initial naming context.
002       Copyright (C) 2000, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.naming;
040    
041    import java.applet.Applet;
042    import java.io.IOException;
043    import java.io.InputStream;
044    import java.net.URL;
045    import java.util.Enumeration;
046    import java.util.HashSet;
047    import java.util.Hashtable;
048    import java.util.Properties;
049    
050    import 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     */
057    public 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    }