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}