Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/jnp/interfaces/NamingContext.java


1   /*
2    * JBoss, the OpenSource J2EE WebOS
3    *
4    * Distributable under LGPL license.
5    * See terms of license at gnu.org.
6    */
7   package org.jnp.interfaces;
8   
9   import java.io.BufferedInputStream;
10  import java.io.ObjectInputStream;
11  import java.io.IOException;
12  import java.lang.ref.WeakReference;
13  import java.lang.reflect.Constructor;
14  import java.lang.reflect.InvocationTargetException;
15  import java.net.DatagramPacket;
16  import java.net.InetAddress;
17  import java.net.MulticastSocket;
18  import java.net.Socket;
19  import java.rmi.ConnectException;
20  import java.rmi.MarshalledObject;
21  import java.util.Arrays;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Enumeration;
25  import java.util.Hashtable;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.StringTokenizer;
29  
30  import javax.naming.Binding;
31  import javax.naming.CannotProceedException;
32  import javax.naming.CommunicationException;
33  import javax.naming.ConfigurationException;
34  import javax.naming.Context;
35  import javax.naming.InvalidNameException;
36  import javax.naming.InitialContext;
37  import javax.naming.LinkRef;
38  import javax.naming.Name;
39  import javax.naming.NamingEnumeration;
40  import javax.naming.NamingException;
41  import javax.naming.NameParser;
42  import javax.naming.NotContextException;
43  import javax.naming.OperationNotSupportedException;
44  import javax.naming.Reference;
45  import javax.naming.Referenceable;
46  import javax.naming.ServiceUnavailableException;
47  import javax.naming.spi.NamingManager;
48  import javax.naming.spi.ResolveResult;
49  import javax.net.SocketFactory;
50  
51  import org.jboss.logging.Logger;
52  
53  /** This class provides the jnp provider Context implementation. It is a
54   * Context interface wrapper for a RMI Naming instance that is obtained from
55   * either the local server instance or by locating the server given by the
56   * Context.PROVIDER_URL value.
57   *
58   * This class also serves as the jnp url resolution context. jnp style urls
59   * passed to the
60   *
61   *   @author oberg
62   *   @author scott.stark@jboss.org
63   *   @version $Revision: 1.18.2.15 $
64   *
65   * <p><b>Revisions:</b><br>
66   * <p><b>2001/09/14: billb</b>
67   * <ol>
68   *   <li> Provider URL can now be a commented delimited list. Will loop through list until a connection is made.
69   * </ol>
70   */
71  public class NamingContext
72     implements Context, java.io.Serializable
73  {
74     // Constants -----------------------------------------------------
75     /** @since 1.7 */
76     static final long serialVersionUID = 8906455608484282128L;
77     /** The javax.net.SocketFactory impl to use for the bootstrap socket */
78     public static final String JNP_SOCKET_FACTORY = "jnp.socketFactory";
79     /** The local address to bind the connected bootstrap socket to */
80     public static final String JNP_LOCAL_ADDRESS = "jnp.localAddress";
81     /** The local port to bind the connected bootstrap socket to */
82     public static final String JNP_LOCAL_PORT = "jnp.localPort";
83     /** A flag to disable the broadcast discovery queries */
84     public static final String JNP_DISABLE_DISCOVERY = "jnp.disableDiscovery";
85     /** The cluster partition discovery should be restricted to */
86     public static final String JNP_PARTITION_NAME = "jnp.partitionName";
87     /** The multicast IP/address to which the discovery query is sent */
88     public static final String JNP_DISCOVERY_GROUP = "jnp.discoveryGroup";
89     /** The port to which the discovery query is sent */
90     public static final String JNP_DISCOVERY_PORT = "jnp.discoveryPort";
91     /** The time in MS to wait for a discovery query response */
92     public static final String JNP_DISCOVERY_TIMEOUT = "jnp.discoveryTimeout";
93  
94     /** The default discovery multicast information */
95     public final static String DEFAULT_DISCOVERY_GROUP_ADDRESS = "230.0.0.4";
96     public final static int DEFAULT_DISCOVERY_GROUP_PORT = 1102;
97     public final static int DEFAULT_DISCOVERY_TIMEOUT = 5000;
98  
99     /** Maximum number of retries on a ConnectException */
100    public static int MAX_RETRIES = 10;
101    /** The JBoss logging interface */
102    private static Logger log = Logger.getLogger(NamingContext.class);
103 
104    // Static --------------------------------------------------------
105    
106    public static Hashtable haServers = new Hashtable ();
107    
108    public static void setHANamingServerForPartition (String partitionName, Naming haServer)
109    {
110       haServers.put (partitionName, haServer);
111    }
112 
113    public static void removeHANamingServerForPartition (String partitionName)
114    {
115       haServers.remove (partitionName);
116    }
117 
118    public static Naming getHANamingServerForPartition (String partitionName)
119    {
120       return (Naming)haServers.get (partitionName);
121    }
122 
123    public static Naming localServer;
124 
125    // Attributes ----------------------------------------------------
126    Naming naming;
127    Hashtable env;
128    Name prefix;
129 
130    NameParser parser = new NamingParser ();
131    
132    // Static --------------------------------------------------------
133    
134    // Cache of naming server stubs
135    // This is a critical optimization in the case where new InitialContext
136    // is performed often. The server stub will be shared between all those
137    // calls, which will improve performance.
138    // Weak references are used so if no contexts use a particular server
139    // it will be removed from the cache.
140    static HashMap cachedServers = new HashMap ();
141    
142    static void addServer (String name, Naming server)
143    {
144       // Add server to map
145       // Clone and synchronize to minimize delay for readers of the map
146       synchronized (NamingContext.class)
147       {
148          HashMap newServers = (HashMap)cachedServers.clone ();
149          newServers.put (name, new WeakReference (server));
150          cachedServers = newServers;
151       }
152    }
153    
154    static Naming getServer(String host, int port, Hashtable serverEnv)
155       throws NamingException
156    {
157       // Check the server cache for a host:port entry
158       String hostKey = host+":"+port;
159       WeakReference ref = (WeakReference)cachedServers.get(hostKey);
160       Naming server;
161       if (ref != null)
162       {
163          server = (Naming) ref.get();
164          if (server != null)
165          {
166             return server;
167          }
168       }
169 
170       // Server not found; add it to cache
171       try
172       {
173          SocketFactory factory = loadSocketFactory(serverEnv);
174          Socket s;
175 
176          try
177          {
178             InetAddress localAddr = null;
179             int localPort = 0;
180             String localAddrStr = (String) serverEnv.get(JNP_LOCAL_ADDRESS);
181             String localPortStr = (String) serverEnv.get(JNP_LOCAL_PORT);
182             if( localAddrStr != null )
183                localAddr = InetAddress.getByName(localAddrStr);
184             if( localPortStr != null )
185                localPort = Integer.parseInt(localPortStr);
186             s = factory.createSocket(host, port, localAddr, localPort);
187          }
188          catch (IOException e)
189          {
190             NamingException ex = new ServiceUnavailableException("Failed to connect to server "+hostKey);
191             ex.setRootCause(e);
192             throw ex;
193          }
194 
195          // Get stub from naming server
196          BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
197          ObjectInputStream in = new ObjectInputStream(bis);
198          MarshalledObject stub = (MarshalledObject) in.readObject();
199          server = (Naming) stub.get();
200          s.close();
201 
202          // Add it to cache
203          addServer(hostKey, server);
204 
205          return server;
206       }
207       catch(IOException e)
208       {
209          NamingException ex = new CommunicationException("Failed to retrieve stub from server "+hostKey);
210          ex.setRootCause(e);
211          throw ex;
212       }
213       catch(Exception e)
214       {
215          NamingException ex = new CommunicationException("Failed to connect to server "+hostKey);
216          ex.setRootCause (e);
217          throw ex;
218       }
219    }
220 
221    /** Create a SocketFactory based on the JNP_SOCKET_FACTORY property in the
222     given env. If JNP_SOCKET_FACTORY is not specified default to the
223     TimedSocketFactory.
224     */
225    static SocketFactory loadSocketFactory(Hashtable serverEnv)
226       throws ClassNotFoundException, IllegalAccessException,
227       InstantiationException, InvocationTargetException
228    {
229       SocketFactory factory = null;
230 
231       // Get the socket factory classname
232       String socketFactoryName = (String) serverEnv.get(JNP_SOCKET_FACTORY);
233       if( socketFactoryName == null ||
234          socketFactoryName.equals(TimedSocketFactory.class.getName()) )
235       {
236          factory = new TimedSocketFactory(serverEnv);
237          return factory;
238       }
239 
240       /* Create the socket factory. Look for a ctor that accepts a
241        Hashtable and if not found use the default ctor.
242        */
243       ClassLoader loader = Thread.currentThread().getContextClassLoader();
244       Class factoryClass = loader.loadClass(socketFactoryName);
245       try
246       {
247          Class[] ctorSig = {Hashtable.class};
248          Constructor ctor = factoryClass.getConstructor(ctorSig);
249          Object[] ctorArgs = {serverEnv};
250          factory = (SocketFactory) ctor.newInstance(ctorArgs);
251       }
252       catch(NoSuchMethodException e)
253       {
254          // Use the default ctor
255          factory = (SocketFactory) factoryClass.newInstance();
256       }
257       return factory;
258    }
259 
260    static void removeServer (Hashtable serverEnv)
261    {
262       String host = "localhost";
263       int port = 1099;
264       
265       // Locate naming service
266       if (serverEnv.get (Context.PROVIDER_URL) != null)
267       {
268          String providerURL = (String)serverEnv.get(Context.PROVIDER_URL);
269 
270          StringTokenizer tokenizer = new StringTokenizer (providerURL, ",");
271          while (tokenizer.hasMoreElements ())
272          {
273             String url = tokenizer.nextToken ();
274 
275             try
276             {
277                // Parse the url into a host:port form, stripping any protocol
278                Name urlAsName = new NamingParser ().parse(url);
279                String server = parseNameForScheme(urlAsName);
280                if( server != null )
281                   url = server;
282                int colon = url.indexOf (':');
283                if( colon < 0 )
284                {
285                   host = url;
286                }
287                else
288                {
289                   host = url.substring (0, colon).trim();
290                   try
291                   {
292                      port = Integer.parseInt (url.substring (colon+1).trim ());
293                   }
294                   catch (Exception ex)
295                   {
296                      // Use default;
297                   }
298                 }
299 
300                // Remove server from map
301                // Clone and synchronize to minimize delay for readers of the map
302                synchronized (NamingContext.class)
303                {
304                   HashMap newServers = (HashMap)cachedServers.clone ();
305                   newServers.remove (host+":"+port);
306                   cachedServers = newServers;
307                }
308 
309             }
310             catch (NamingException ignored) {}
311 
312 
313          }
314       }
315       else
316       {
317          // Don't do anything for local server
318       }
319       
320    }
321    
322    /** Called to remove any url scheme atoms and extract the naming
323     * service hostname:port information.
324     * @param n the name component to the parsed. After returning n will
325     * have all scheme related atoms removed.
326     * @return the naming service hostname:port information string if name
327     * contained the host information.
328     */
329    static String parseNameForScheme (Name n) throws InvalidNameException
330    {
331       String serverInfo = null;
332       if( n.size () > 0 )
333       {
334          String scheme = n.get (0);
335          int schemeLength = 0;
336          if( scheme.startsWith ("java:") )
337             schemeLength = 5;
338          else if( scheme.startsWith ("jnp:") )
339             schemeLength = 4;
340          else if( scheme.startsWith ("jnps:") )
341             schemeLength = 5;
342          else if( scheme.startsWith ("jnp-http:") )
343             schemeLength = 9;
344          else if( scheme.startsWith ("jnp-https:") )
345             schemeLength = 10;
346          if( schemeLength > 0 )
347          {
348             String suffix = scheme.substring (schemeLength);
349             if( suffix.length () == 0 )
350             {
351                // Scheme was "url:/..."
352                n.remove (0);
353                if( n.size () > 1 && n.get (0).equals ("") )
354                {
355                   // Scheme was "url://hostname:port/..."
356                   // Get hostname:port value for the naming server
357                   serverInfo = n.get (1);
358                   n.remove (0);
359                   n.remove (0);
360                   // If n is a empty atom remove it or else a '/' will result
361                   if( n.size () == 1 && n.get (0).length () == 0 )
362                      n.remove (0);
363                }
364             }
365             else
366             {
367                // Scheme was "url:foo" -> reinsert "foo"
368                n.remove (0);
369                n.add (0, suffix);
370             }
371          }
372       }
373       return serverInfo;
374    }
375    
376    public static void setLocal (Naming server)
377    {
378       localServer = server;
379    }
380    
381    // Constructors --------------------------------------------------
382    public NamingContext (Hashtable e, Name baseName, Naming server)
383       throws NamingException
384    {
385       if (baseName == null)
386          this.prefix = parser.parse ("");
387       else
388          this.prefix = baseName;
389       
390       if (e != null)
391          this.env = (Hashtable)e.clone ();
392       else
393          this.env = new Hashtable ();
394       
395       this.naming = server;
396    }
397    
398    // Public --------------------------------------------------------
399    public Naming getNaming()
400    {
401       return this.naming;
402    }
403    public void setNaming(Naming server)
404    {
405       this.naming = server;
406    }
407 
408    // Context implementation ----------------------------------------
409    public void rebind (String name, Object obj)
410       throws NamingException
411    {
412       rebind (getNameParser (name).parse (name), obj);
413    }
414    
415    public void rebind (Name name, Object obj)
416       throws NamingException
417    {
418       Hashtable refEnv = getEnv (name);
419       checkRef (refEnv);
420 
421       try
422       {
423          String className;
424          
425          // Referenceable
426          if (obj instanceof Referenceable)
427             obj = ((Referenceable)obj).getReference ();
428          
429          if (!(obj instanceof Reference))
430          {
431             className = obj.getClass ().getName ();
432             // Normal object - serialize using a MarshalledValuePair
433             obj = new MarshalledValuePair(obj);
434          }
435          else
436          {
437             className = ((Reference)obj).getClassName ();
438          }
439          naming.rebind (getAbsoluteName (name),obj, className);
440       }
441       catch (CannotProceedException cpe)
442       {
443          cpe.setEnvironment (refEnv);
444          Context cctx = NamingManager.getContinuationContext (cpe);
445          cctx.rebind (cpe.getRemainingName (), obj);
446       } catch (IOException e)
447       {
448          naming = null;
449          removeServer (refEnv);
450          NamingException ex = new CommunicationException ();
451          ex.setRootCause (e);
452          throw ex;
453       }
454    }
455    
456    public void bind (String name, Object obj)
457    throws NamingException
458    {
459       bind (getNameParser (name).parse (name), obj);
460    }
461    
462    public void bind (Name name, Object obj)
463       throws NamingException
464    {
465       Hashtable refEnv = getEnv (name);
466       checkRef (refEnv);
467       
468       try
469       {
470          String className;
471          
472          // Referenceable
473          if (obj instanceof Referenceable)
474             obj = ((Referenceable)obj).getReference ();
475          
476          if (!(obj instanceof Reference))
477          {
478             className = obj.getClass ().getName ();
479             
480             // Normal object - serialize using a MarshalledValuePair
481             obj = new MarshalledValuePair(obj);
482          }
483          else
484          {
485             className = ((Reference)obj).getClassName ();
486          }
487          name = getAbsoluteName (name);
488          naming.bind (name,obj, className);
489       } catch (CannotProceedException cpe)
490       {
491          cpe.setEnvironment (refEnv);
492          Context cctx = NamingManager.getContinuationContext (cpe);
493          cctx.bind (cpe.getRemainingName (), obj);
494       } catch (IOException e)
495       {
496          naming = null;
497          removeServer (refEnv);
498          NamingException ex = new CommunicationException ();
499          ex.setRootCause (e);
500          throw ex;
501       }
502    }
503    
504    public Object lookup (String name)
505    throws NamingException
506    {
507       return lookup (getNameParser (name).parse (name));
508    }
509    
510    public Object lookup (Name name)
511       throws NamingException
512    {
513       Hashtable refEnv = getEnv (name);
514       checkRef (refEnv);
515       
516       // Empty?
517       if (name.isEmpty ())
518          return new NamingContext (refEnv, prefix, naming);
519       
520       try
521       {
522          Name n = getAbsoluteName (name);
523          Object res = null;
524          for (int i = 0; i < MAX_RETRIES; i++)
525          {
526             try
527             {
528                res = naming.lookup (n);
529                break;
530             }
531             catch (ConnectException ce)
532             {
533                // We may overload server so sleep and retry
534                if (i + 1 < MAX_RETRIES) 
535                {
536                   try
537                   {
538                      Thread.sleep(1);
539                   }
540                   catch (InterruptedException ignored) {}
541                   continue;
542                }
543                // Throw the exception to flush the bad server
544                throw ce;
545             }
546          }
547          if (res instanceof MarshalledValuePair)
548          {
549             MarshalledValuePair mvp = (MarshalledValuePair) res;
550             return mvp.get();
551          }
552          else if(res instanceof MarshalledObject)
553          {
554             MarshalledObject mo = (MarshalledObject) res;
555             return mo.get();
556          }
557          else if (res instanceof Context)
558          {
559             // Add env
560             Enumeration keys = refEnv.keys();
561             while (keys.hasMoreElements())
562             {
563                String key = (String)keys.nextElement ();
564                ((Context)res).addToEnvironment(key, refEnv.get (key));
565             }
566             return res;
567          }
568          else if (res instanceof ResolveResult)
569          {
570             // Dereference partial result
571             try
572             {
573                Object resolveRes = ((ResolveResult)res).getResolvedObj ();
574                if (resolveRes instanceof LinkRef)
575                {
576                   String ref = ((LinkRef)resolveRes).getLinkName ();
577                   Context ctx;
578                   try
579                   {
580                      ctx = (Context) resolveLink(resolveRes, null);
581                      return ctx.lookup (((ResolveResult)res).getRemainingName ());
582                   } catch (ClassCastException e)
583                   {
584                      throw new NotContextException (ref + " is not a context");
585                   }
586                } else
587                {
588                   try
589                   {
590                      Context ctx = (Context)NamingManager.getObjectInstance (resolveRes,
591                      getAbsoluteName (name),
592                      this,
593                      refEnv);
594                      return ctx.lookup (((ResolveResult)res).getRemainingName ());
595                   } catch (ClassCastException e)
596                   {
597                      throw new NotContextException ();
598                   }
599                }
600             } catch (NamingException e)
601             {
602                throw e;
603             } catch (Exception e)
604             {
605                NamingException ex = new NamingException ("Could not dereference object");
606                ex.setRootCause (e);
607                throw ex;
608             }
609          }
610          else if (res instanceof LinkRef)
611          {
612             // Dereference link
613             res = resolveLink(res, refEnv);
614          }
615          else if (res instanceof Reference)
616          {
617             // Dereference object
618             try
619             {
620                res = NamingManager.getObjectInstance (res,
621                getAbsoluteName (name),
622                this,
623                refEnv);
624                if( res instanceof LinkRef )
625                   res = resolveLink(res, refEnv);
626             } catch (NamingException e)
627             {
628                throw e;
629             } catch (Exception e)
630             {
631                NamingException ex = new NamingException ("Could not dereference object");
632                ex.setRootCause (e);
633                throw ex;
634             }
635          }
636          
637          return res;
638       } catch (CannotProceedException cpe)
639       {
640          cpe.setEnvironment (refEnv);
641          Context cctx = NamingManager.getContinuationContext (cpe);
642          return cctx.lookup (cpe.getRemainingName ());
643       } catch (IOException e)
644       {
645          naming = null;
646          removeServer (refEnv);
647          NamingException ex = new CommunicationException ();
648          ex.setRootCause (e);
649          throw ex;
650       } catch (ClassNotFoundException e)
651       {
652          NamingException ex = new CommunicationException ();
653          ex.setRootCause (e);
654          throw ex;
655       }
656    }
657    
658    public void unbind (String name)
659    throws NamingException
660    {
661       unbind (getNameParser (name).parse (name));
662    }
663    
664    
665    public void unbind (Name name)
666    throws NamingException
667    {
668       Hashtable refEnv = getEnv (name);
669       checkRef (refEnv);
670       
671       try
672       {
673          naming.unbind (getAbsoluteName (name));
674       } catch (CannotProceedException cpe)
675       {
676          cpe.setEnvironment (refEnv);
677          Context cctx = NamingManager.getContinuationContext (cpe);
678          cctx.unbind (cpe.getRemainingName ());
679       } catch (IOException e)
680       {
681          naming = null;
682          removeServer (refEnv);
683          NamingException ex = new CommunicationException ();
684          ex.setRootCause (e);
685          throw ex;
686       }
687    }
688    
689    public void rename (String oldname, String newname)
690    throws NamingException
691    {
692       rename (getNameParser (oldname).parse (oldname), getNameParser (newname).parse (newname));
693    }
694    
695    public void rename (Name oldName, Name newName)
696    throws NamingException
697    {
698       bind (newName,lookup (oldName));
699       unbind (oldName);
700    }
701    
702    public NamingEnumeration list (String name)
703    throws NamingException
704    {
705       return list (getNameParser (name).parse (name));
706    }
707    
708    public NamingEnumeration list (Name name)
709    throws NamingException
710    {
711       Hashtable refEnv = getEnv (name);
712       checkRef (refEnv);
713       
714       try
715       {
716          return new NamingEnumerationImpl (naming.list (getAbsoluteName (name)));
717       } catch (CannotProceedException cpe)
718       {
719          cpe.setEnvironment (refEnv);
720          Context cctx = NamingManager.getContinuationContext (cpe);
721          return cctx.list (cpe.getRemainingName ());
722       } catch (IOException e)
723       {
724          naming = null;
725          removeServer (refEnv);
726          NamingException ex = new CommunicationException ();
727          ex.setRootCause (e);
728          throw ex;
729       }
730    }
731    
732    public NamingEnumeration listBindings (String name)
733    throws NamingException
734    {
735       return listBindings (getNameParser (name).parse (name));
736    }
737    
738    public NamingEnumeration listBindings (Name name)
739    throws NamingException
740    {
741       Hashtable refEnv = getEnv (name);
742       checkRef (refEnv);
743       
744       try
745       {
746          // Get list
747          Collection bindings = naming.listBindings (getAbsoluteName (name));
748          Collection realBindings = new ArrayList (bindings.size ());
749          
750          // Convert marshalled objects
751          Iterator enum = bindings.iterator ();
752          while (enum.hasNext ())
753          {
754             Binding binding = (Binding)enum.next ();
755             Object obj = binding.getObject ();
756             if (obj instanceof MarshalledValuePair)
757             {
758                try
759                {
760                   obj = ((MarshalledValuePair)obj).get ();
761                }
762                catch (ClassNotFoundException e)
763                {
764                   NamingException ex = new CommunicationException ();
765                   ex.setRootCause (e);
766                   throw ex;
767                }
768             }
769             else if(obj instanceof MarshalledObject)
770             {
771                try
772                {
773                   obj = ((MarshalledObject)obj).get ();
774                }
775                catch (ClassNotFoundException e)
776                {
777                   NamingException ex = new CommunicationException ();
778                   ex.setRootCause (e);
779                   throw ex;
780                }
781             }
782             realBindings.add (new Binding (binding.getName (), binding.getClassName (), obj));
783          }
784          
785          // Return transformed list of bindings
786          return new NamingEnumerationImpl (realBindings);
787       } catch (CannotProceedException cpe)
788       {
789          cpe.setEnvironment (refEnv);
790          Context cctx = NamingManager.getContinuationContext (cpe);
791          return cctx.listBindings (cpe.getRemainingName ());
792       } catch (IOException e)
793       {
794          naming = null;
795          removeServer (refEnv);
796          NamingException ex = new CommunicationException ();
797          ex.setRootCause (e);
798          throw ex;
799       }
800    }
801    
802    public String composeName (String name, String prefix)
803    throws NamingException
804    {
805       Name result = composeName (parser.parse (name),
806       parser.parse (prefix));
807       return result.toString ();
808    }
809    
810    public Name composeName (Name name, Name prefix)
811    throws NamingException
812    {
813       Name result = (Name)(prefix.clone ());
814       result.addAll (name);
815       return result;
816    }
817    
818    public NameParser getNameParser (String name)
819    throws NamingException
820    {
821       return parser;
822    }
823    
824    public NameParser getNameParser (Name name)
825    throws NamingException
826    {
827       return getNameParser (name.toString ());
828    }
829    
830    public Context createSubcontext (String name)
831    throws NamingException
832    {
833       return createSubcontext (getNameParser (name).parse (name));
834    }
835    
836    public Context createSubcontext (Name name)
837    throws NamingException
838    {
839       if( name.size () == 0 )
840          throw new InvalidNameException ("Cannot pass an empty name to createSubcontext");
841       
842       Hashtable refEnv = getEnv (name);
843       checkRef (refEnv);
844       try
845       {
846          name = getAbsoluteName (name);
847          return naming.createSubcontext (name);
848       }
849       catch (CannotProceedException cpe)
850       {
851          cpe.setEnvironment (refEnv);
852          Context cctx = NamingManager.getContinuationContext (cpe);
853          return cctx.createSubcontext (cpe.getRemainingName ());
854       }
855       catch (IOException e)
856       {
857          naming = null;
858          removeServer (refEnv);
859          NamingException ex = new CommunicationException ();
860          ex.setRootCause (e);
861          throw ex;
862       }
863    }
864    
865    public Object addToEnvironment (String propName, Object propVal)
866       throws NamingException
867    {
868       Object old = env.get (propName);
869       env.put (propName,propVal);
870       return old;
871    }
872    
873    public Object removeFromEnvironment (String propName)
874       throws NamingException
875    {
876       return env.remove (propName);
877    }
878    
879    public Hashtable getEnvironment ()
880       throws NamingException
881    {
882       return env;
883    }
884    
885    public void close ()
886       throws NamingException
887    {
888       env = null;
889       naming = null;
890    }
891    
892    public String getNameInNamespace ()
893    throws NamingException
894    {
895       return prefix.toString ();
896    }
897    
898    public void destroySubcontext (String name)
899    throws NamingException
900    {
901       throw new OperationNotSupportedException ();
902    }
903    
904    public void destroySubcontext (Name name)
905    throws NamingException
906    {
907       throw new OperationNotSupportedException ();
908    }
909    
910    public Object lookupLink (String name)
911    throws NamingException
912    {
913       return lookupLink (getNameParser (name).parse (name));
914    }
915    
916    /** Lookup the object referred to by name but don't dereferrence the final
917     * component. This really just involves returning the raw value returned by
918     * the Naming.lookup() method.
919     * @return the raw object bound under name.
920     */
921    public Object lookupLink (Name name)
922       throws NamingException
923    {
924       if( name.isEmpty () )
925          return lookup (name);
926       
927       Object link = null;
928       try
929       {
930          Name n = getAbsoluteName (name);
931          link = naming.lookup (n);
932          if (!(link instanceof LinkRef) && link instanceof Reference)
933             link = NamingManager.getObjectInstance (link, n, this, null);
934       }
935       catch(IOException e)
936       {
937          naming = null;
938          removeServer (env);
939          NamingException ex = new CommunicationException ();
940          ex.setRootCause (e);
941          throw ex;
942       }
943       catch(Exception e)
944       {
945          NamingException ex = new NamingException ("Could not lookup link");
946          ex.setRemainingName(name);
947          ex.setRootCause(e);
948          throw ex;
949       }
950       return link;
951    }
952 
953    protected Object resolveLink(Object res, Hashtable refEnv)
954       throws NamingException
955    {
956       Object linkResult = null;
957       try
958       {
959          LinkRef link = (LinkRef) res;
960          String ref = link.getLinkName();
961          if (ref.startsWith ("./"))
962             linkResult = lookup (ref.substring (2));
963          else if( refEnv != null )
964             linkResult = new InitialContext(refEnv).lookup (ref);
965          else
966             linkResult = new InitialContext().lookup (ref);
967       }
968       catch (Exception e)
969       {
970          NamingException ex = new NamingException ("Could not dereference object");
971          ex.setRootCause (e);
972          throw ex;
973       }
974       return linkResult;
975    }
976 
977    // Private -------------------------------------------------------
978    
979    /** This methods sends a broadcast message on the network and asks and
980     * HA-JNDI server to sent it the HA-JNDI stub
981     */
982    private Naming discoverServer(Hashtable serverEnv) throws NamingException
983    {
984       boolean trace = log.isTraceEnabled();
985       // Check if discovery should be done
986       String disableDiscovery = (String) serverEnv.get(JNP_DISABLE_DISCOVERY);
987       if( Boolean.valueOf(disableDiscovery) == Boolean.TRUE )
988       {
989          if( trace )
990             log.trace("Skipping discovery due to disable flag");
991          return null;
992       }
993       
994       // we first try to discover the server locally
995       //
996       String partitionName = (String) serverEnv.get(JNP_PARTITION_NAME);
997       Naming server = null;
998       if (partitionName != null)
999       {
1000         server = getHANamingServerForPartition (partitionName);
1001         if (server != null)
1002            return server;
1003      }
1004      
1005      // We next broadcast a HelloWorld datagram (multicast)
1006      // Any listening server will answer with its IP address:port in another datagram
1007      // we will then use this to make a standard "lookup"
1008      //
1009      MulticastSocket s = null;
1010      InetAddress iaGroup = null;
1011      try
1012      {
1013         String group = DEFAULT_DISCOVERY_GROUP_ADDRESS;
1014         int port = DEFAULT_DISCOVERY_GROUP_PORT;
1015         int timeout = DEFAULT_DISCOVERY_TIMEOUT;
1016
1017         String discoveryGroup = (String) serverEnv.get(JNP_DISCOVERY_GROUP);
1018         if( discoveryGroup != null )
1019            group = discoveryGroup;
1020         String discoveryTimeout = (String) serverEnv.get(JNP_DISCOVERY_TIMEOUT);
1021         if( discoveryTimeout == null )
1022         {
1023            // Check the old property name
1024            discoveryTimeout = (String) serverEnv.get("DISCOVERY_TIMEOUT");
1025         }
1026         if (discoveryTimeout != null && !discoveryTimeout.equals (""))
1027            timeout = Integer.parseInt (discoveryTimeout);
1028
1029         String discoveryGroupPort = (String) serverEnv.get(JNP_DISCOVERY_PORT);
1030         if( discoveryGroupPort == null )
1031         {
1032            // Check the old property name
1033            discoveryGroupPort = (String) serverEnv.get("DISCOVERY_GROUP");
1034         }
1035         if (discoveryGroupPort != null && !discoveryGroupPort.equals (""))
1036         {
1037            int colon = discoveryGroupPort.indexOf (':');
1038            if( colon < 0 )
1039            {
1040               // No group given, just the port
1041               try
1042               {
1043                  port = Integer.parseInt (discoveryGroupPort);
1044               }
1045               catch (Exception ex)
1046               {
1047                  log.warn("Failed to parse port: "+discoveryGroupPort, ex);
1048               }
1049            }
1050            else
1051            {
1052               // The old group:port syntax was given
1053               group = discoveryGroupPort.substring (0, colon);
1054               String portStr = discoveryGroupPort.substring (colon+1);
1055               try
1056               {
1057                  port = Integer.parseInt (portStr);
1058               }
1059               catch (Exception ex)
1060               {
1061                  log.warn("Failed to parse port: "+portStr, ex);
1062               }
1063            }
1064         }
1065         
1066         iaGroup = InetAddress.getByName (group);
1067         s = new MulticastSocket (port);
1068         s.setSoTimeout (timeout);
1069         s.joinGroup(iaGroup);
1070
1071         DatagramPacket packet;
1072         // Send a request optionally restricted to a cluster partition
1073         StringBuffer data = new StringBuffer("GET_ADDRESS");
1074         if( partitionName != null )
1075            data.append(":"+partitionName);
1076         byte[] buf = data.toString().getBytes();
1077         packet = new DatagramPacket (buf, buf.length, iaGroup, port);
1078         if( trace )
1079            log.trace("Sending discovery packet("+data+") to: "+iaGroup+":"+port);
1080         s.send (packet);
1081         // Look for a reply
1082         // IP address + port number = 128.128.128.128:65535 => (12+3) + 1 + (5) = 21
1083
1084         buf = new byte[50];
1085         packet = new DatagramPacket (buf, buf.length);
1086         s.receive (packet);
1087         String myServer = new String (packet.getData ()).trim();
1088         if( trace )
1089            log.trace("Received answer packet: "+myServer);
1090         while (myServer != null && myServer.startsWith("GET_ADDRESS"))
1091         {
1092            Arrays.fill(buf, (byte) 0);
1093            s.receive (packet);
1094            byte[] reply = packet.getData();
1095            myServer = new String (reply).trim();
1096            if( trace )
1097               log.trace("Received answer packet: "+myServer);
1098         }
1099         String serverHost;
1100         int serverPort;
1101
1102         int colon = myServer.indexOf (':');
1103         if( colon >= 0 )
1104         {
1105            serverHost = myServer.substring (0, colon);
1106            serverPort = Integer.valueOf (myServer.substring (colon + 1)).intValue ();
1107            server = getServer (serverHost, serverPort, serverEnv);
1108         }
1109         return server;
1110      }
1111      catch (IOException e)
1112      {
1113         if( trace )
1114            log.trace("Discovery failed", e);
1115         NamingException ex = new CommunicationException (e.getMessage ());
1116         ex.setRootCause (e);
1117         throw ex;
1118      }
1119      finally
1120      {
1121         try
1122         {
1123            if (s != null)
1124               s.leaveGroup (iaGroup);
1125         }
1126         catch(Exception ignore)
1127         {
1128         }
1129         try
1130         {
1131            if (s != null)
1132               s.close ();
1133         }
1134         catch(Exception ignore)
1135         {
1136         }
1137      }
1138   }
1139
1140   private void checkRef(Hashtable refEnv)
1141      throws NamingException
1142   {
1143      if (naming == null)
1144      {
1145         String host = "localhost";
1146         int port = 1099;
1147         
1148         // Locate naming service
1149         String urls = (String) refEnv.get (Context.PROVIDER_URL);
1150         if (urls != null && urls.length () > 0)
1151         {
1152            StringTokenizer tokenizer = new StringTokenizer (urls, ",");
1153            while (tokenizer.hasMoreElements ())
1154            {
1155               String url = tokenizer.nextToken ();
1156               // Parse the url into a host:port form, stripping any protocol
1157               Name urlAsName = getNameParser("").parse(url);
1158               String server = parseNameForScheme(urlAsName);
1159               if( server != null )
1160                  url = server;
1161               int colon = url.indexOf (':');
1162               if( colon < 0 )
1163               {
1164                  host = url;
1165               }
1166               else
1167               {
1168                  host = url.substring (0, colon).trim();
1169                  try
1170                  {
1171                     port = Integer.parseInt (url.substring (colon+1).trim ());
1172                  }
1173                  catch (Exception ex)
1174                  {
1175                     // Use default;
1176                  }
1177               }
1178               try
1179               {
1180                  // Get server from cache
1181                  naming = getServer (host, port, refEnv);
1182               }
1183               catch(Exception e)
1184               {
1185                  log.warn("Failed to connect to "+host+":"+port, e);
1186               }
1187            }
1188
1189            // If there is still no 
1190            if (naming == null)
1191            {
1192               naming = discoverServer (refEnv);
1193               if (naming == null)
1194                  throw new CommunicationException ("Could not obtain connection to any of these urls: " + urls);
1195            }
1196         }
1197         else
1198         {
1199            // If we are in a clustering scenario, the client code may request a context
1200            // for a *specific* HA-JNDI service (i.e. linked to a *specific* partition)
1201            // EVEN if the lookup is done inside a JBoss VM. For example, a JBoss service
1202            // may do a lookup on a HA-JNDI service running on another host *without*
1203            // explicitly providing a PROVIDER_URL but simply by providing a JNP_PARTITON_NAME
1204            // parameter so that dynamic discovery can be used
1205            //
1206            String jnpPartitionName = (String)refEnv.get (JNP_PARTITION_NAME);
1207            if (jnpPartitionName != null)
1208            {
1209               // the client is requesting for a specific partition name
1210               // 
1211               naming = discoverServer (refEnv);
1212               if (naming == null)
1213                  throw new ConfigurationException 
1214                     ("No valid context could be build for jnp.partitionName=" + jnpPartitionName);
1215            }
1216            else
1217            {
1218               // Use server in same JVM
1219               naming = localServer;
1220
1221               if (naming == null)
1222               {
1223                  naming = discoverServer (refEnv);
1224                  if (naming == null)
1225                     // Local, but no local JNDI provider found!
1226                     throw new ConfigurationException ("No valid Context.PROVIDER_URL was found");
1227               }
1228            }
1229         }
1230      }
1231   }
1232   
1233   private Name getAbsoluteName (Name n)
1234      throws NamingException
1235   {
1236      if (n.isEmpty ())
1237         return composeName (n,prefix);
1238      else if (n.get (0).toString ().equals ("")) // Absolute name
1239         return n.getSuffix (1);
1240      else // Add prefix
1241         return composeName (n,prefix);
1242   }
1243   
1244   private Hashtable getEnv (Name n)
1245      throws InvalidNameException
1246   {
1247      Hashtable nameEnv = env;
1248      String serverInfo = parseNameForScheme (n);
1249      if( serverInfo != null )
1250      {
1251         // Set hostname:port value for the naming server
1252         nameEnv = (Hashtable)env.clone ();
1253         nameEnv.put (Context.PROVIDER_URL, serverInfo);
1254      }
1255      return nameEnv;
1256   }
1257
1258   // Inner classes -------------------------------------------------
1259}