1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jnp.interfaces;
23
24 import java.io.BufferedInputStream;
25 import java.io.IOException;
26 import java.io.ObjectInputStream;
27 import java.lang.ref.WeakReference;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.InvocationTargetException;
30 import java.net.DatagramPacket;
31 import java.net.InetAddress;
32 import java.net.MulticastSocket;
33 import java.net.Socket;
34 import java.net.InetSocketAddress;
35 import java.rmi.ConnectException;
36 import java.rmi.MarshalledObject;
37 import java.rmi.NoSuchObjectException;
38 import java.rmi.RemoteException;
39 import java.security.AccessController;
40 import java.security.PrivilegedActionException;
41 import java.security.PrivilegedExceptionAction;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collection;
45 import java.util.Enumeration;
46 import java.util.Hashtable;
47 import java.util.Iterator;
48 import java.util.StringTokenizer;
49 import java.util.concurrent.ConcurrentHashMap;
50
51 import javax.naming.Binding;
52 import javax.naming.CannotProceedException;
53 import javax.naming.CommunicationException;
54 import javax.naming.ConfigurationException;
55 import javax.naming.Context;
56 import javax.naming.InitialContext;
57 import javax.naming.InvalidNameException;
58 import javax.naming.LinkRef;
59 import javax.naming.Name;
60 import javax.naming.NameParser;
61 import javax.naming.NamingEnumeration;
62 import javax.naming.NamingException;
63 import javax.naming.NotContextException;
64 import javax.naming.ContextNotEmptyException;
65 import javax.naming.Reference;
66 import javax.naming.Referenceable;
67 import javax.naming.ServiceUnavailableException;
68 import javax.naming.event.EventContext;
69 import javax.naming.event.NamingListener;
70 import javax.naming.spi.NamingManager;
71 import javax.naming.spi.ResolveResult;
72 import javax.net.SocketFactory;
73
74 import org.jboss.logging.Logger;
75
76 /**
77 * This class provides the jnp provider Context implementation. It is a Context
78 * interface wrapper for a RMI Naming instance that is obtained from either the
79 * local server instance or by locating the server given by the
80 * Context.PROVIDER_URL value.
81 *
82 * This class also serves as the jnp url resolution context. jnp style urls
83 * passed to the
84 * @author oberg
85 * @author scott.stark@jboss.org
86 * @version $Revision: 88357 $
87 */
88 public class NamingContext
89 implements EventContext, java.io.Serializable
90 {
91 // Constants -----------------------------------------------------
92 /**
93 * @since 1.7
94 */
95 static final long serialVersionUID = 8906455608484282128L;
96 /**
97 * The javax.net.SocketFactory impl to use for the bootstrap socket
98 */
99 public static final String JNP_SOCKET_FACTORY = "jnp.socketFactory";
100 /**
101 * The local address to bind the connected bootstrap socket to
102 */
103 public static final String JNP_LOCAL_ADDRESS = "jnp.localAddress";
104 /**
105 * The local port to bind the connected bootstrap socket to
106 */
107 public static final String JNP_LOCAL_PORT = "jnp.localPort";
108 /**
109 * A flag to disable the broadcast discovery queries
110 */
111 public static final String JNP_DISABLE_DISCOVERY = "jnp.disableDiscovery";
112 /**
113 * The cluster partition discovery should be restricted to
114 */
115 public static final String JNP_PARTITION_NAME = "jnp.partitionName";
116 /**
117 * The multicast IP/address to which the discovery query is sent
118 */
119 public static final String JNP_DISCOVERY_GROUP = "jnp.discoveryGroup";
120 /**
121 * The port to which the discovery query is sent
122 */
123 public static final String JNP_DISCOVERY_PORT = "jnp.discoveryPort";
124
125 /** The time-to-live for the multicast discovery packets */
126 public static final String JNP_DISCOVERY_TTL = "jnp.discoveryTTL";
127
128 /**
129 * The time in MS to wait for a discovery query response
130 */
131 public static final String JNP_DISCOVERY_TIMEOUT = "jnp.discoveryTimeout";
132 /**
133 * An internal property added by parseNameForScheme if the input name uses a
134 * url prefix that was removed during cannonicalization. This is needed to
135 * avoid modification of the incoming Name.
136 */
137 public static final String JNP_PARSED_NAME = "jnp.parsedName";
138 /**
139 * A flag indicating the style of names passed to NamingManager method.
140 * True for api expected relative names, false for absolute names as used
141 * historically by the jboss naming implementation.
142 */
143 public static final String JNP_USE_RELATIVE_NAME = "jnp.useRelativeName";
144 /**
145 * An integer that controls the number of connection retry attempts will
146 * be made on the initial connection to the naming server. This only applies
147 * to ConnectException failures. A value <= 1 means that only one attempt
148 * will be made.
149 */
150 public static final String JNP_MAX_RETRIES = "jnp.maxRetries";
151 /**
152 * The Naming instance to use for the root Context creation
153 */
154 public static final String JNP_NAMING_INSTANCE = "jnp.namingInstance";
155 /**
156 * The name to associate with Naming instance to use for the root Context
157 */
158 public static final String JNP_NAMING_INSTANCE_NAME = "jnp.namingInstanceName";
159
160 /**
161 * Global JNP disable discovery system property: -Djboss.global.jnp.disableDiscover=[true|false]
162 * At the VM level, this property controls how disable discovery behaves in
163 * absence of per context jnp.disableDiscovery property.
164 */
165 private static final boolean GLOBAL_JNP_DISABLE_DISCOVERY = Boolean.valueOf(System.getProperty("jboss.global.jnp.disableDiscovery", "false"));
166
167 /**
168 * The default discovery multicast information
169 */
170 public final static String DEFAULT_DISCOVERY_GROUP_ADDRESS = "230.0.0.4";
171 public final static int DEFAULT_DISCOVERY_GROUP_PORT = 1102;
172 public final static int DEFAULT_DISCOVERY_TIMEOUT = 5000;
173
174 /**
175 * An obsolete constant replaced by the JNP_MAX_RETRIES value
176 */
177 public static int MAX_RETRIES = 1;
178 /**
179 * The JBoss logging interface
180 */
181 private static Logger log = Logger.getLogger(NamingContext.class);
182
183 // Static --------------------------------------------------------
184 /** HAJNDI keyed by partition name */
185 private static Hashtable<String, Naming> haServers = new Hashtable<String, Naming>();
186 private static RuntimePermission GET_HA_NAMING_SERVER = new RuntimePermission("org.jboss.naming.NamingContext.getHANamingServerForPartition");
187 private static RuntimePermission SET_HA_NAMING_SERVER = new RuntimePermission("org.jboss.naming.NamingContext.setHANamingServerForPartition");
188 public static void setHANamingServerForPartition(String partitionName, Naming haServer)
189 {
190 SecurityManager security = System.getSecurityManager();
191 if(security != null)
192 security.checkPermission(SET_HA_NAMING_SERVER);
193 haServers.put(partitionName, haServer);
194 }
195
196 public static void removeHANamingServerForPartition(String partitionName)
197 {
198 SecurityManager security = System.getSecurityManager();
199 if(security != null)
200 security.checkPermission(SET_HA_NAMING_SERVER);
201 haServers.remove(partitionName);
202 }
203
204 public static Naming getHANamingServerForPartition(String partitionName)
205 {
206 SecurityManager security = System.getSecurityManager();
207 if(security != null)
208 security.checkPermission(GET_HA_NAMING_SERVER);
209 return (Naming) haServers.get(partitionName);
210 }
211
212 /**
213 * The jvm local server used for non-transport access to the naming
214 * server
215 * @see #checkRef(Hashtable)
216 * @see {@linkplain LocalOnlyContextFactory}
217 */
218 private static Naming localServer;
219 private static RuntimePermission GET_LOCAL_SERVER = new RuntimePermission("org.jboss.naming.NamingContext.getLocal");
220 private static RuntimePermission SET_LOCAL_SERVER = new RuntimePermission("org.jboss.naming.NamingContext.setLocal");
221 private static int HOST_INDEX = 0;
222 private static int PORT_INDEX = 1;
223
224 // Attributes ----------------------------------------------------
225 Naming naming;
226 Hashtable env;
227 Name prefix;
228
229 NameParser parser = new NamingParser();
230
231 // Static --------------------------------------------------------
232
233 // Cache of naming server stubs
234 // This is a critical optimization in the case where new InitialContext
235 // is performed often. The server stub will be shared between all those
236 // calls, which will improve performance.
237 // Weak references are used so if no contexts use a particular server
238 // it will be removed from the cache.
239 static ConcurrentHashMap<InetSocketAddress, WeakReference<Naming>> cachedServers
240 = new ConcurrentHashMap<InetSocketAddress, WeakReference<Naming>>();
241
242 /**
243 * @deprecated use {@link #addServer(InetSocketAddress, Naming)}
244 * @param name
245 * @param server
246 */
247 static void addServer(String name, Naming server)
248 {
249 Object[] hostAndPort = {name, 0};
250 parseHostPort(name, hostAndPort, 0);
251 String host = (String) hostAndPort[HOST_INDEX];
252 Integer port = (Integer) hostAndPort[PORT_INDEX];
253 InetSocketAddress addr = new InetSocketAddress(host, port);
254 addServer(addr, server);
255 }
256 static void addServer(InetSocketAddress addr, Naming server)
257 {
258 // Add server to map
259 synchronized (NamingContext.class)
260 {
261 WeakReference<Naming> ref = new WeakReference<Naming>(server);
262 cachedServers.put(addr, ref);
263 }
264 }
265
266 static Naming getServer(String host, int port, Hashtable serverEnv)
267 throws NamingException
268 {
269 // Check the server cache for a host:port entry
270 InetSocketAddress key = new InetSocketAddress(host, port);
271 WeakReference<Naming> ref = cachedServers.get(key);
272 Naming server;
273 if (ref != null)
274 {
275 server = (Naming) ref.get();
276 if (server != null)
277 {
278 // JBAS-4622. Ensure the env for the request has the
279 // hostKey so we can remove the cache entry if there is a failure
280 serverEnv.put("hostKey", key);
281 return server;
282 }
283 }
284
285 // Server not found; add it to cache
286 try
287 {
288 SocketFactory factory = loadSocketFactory(serverEnv);
289 Socket s;
290
291 try
292 {
293 InetAddress localAddr = null;
294 int localPort = 0;
295 String localAddrStr = (String) serverEnv.get(JNP_LOCAL_ADDRESS);
296 String localPortStr = (String) serverEnv.get(JNP_LOCAL_PORT);
297 if (localAddrStr != null)
298 localAddr = InetAddress.getByName(localAddrStr);
299 if (localPortStr != null)
300 localPort = Integer.parseInt(localPortStr);
301 s = factory.createSocket(host, port, localAddr, localPort);
302 }
303 catch (IOException e)
304 {
305 NamingException ex = new ServiceUnavailableException("Failed to connect to server " + key);
306 ex.setRootCause(e);
307 throw ex;
308 }
309
310 // Get stub from naming server
311 BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
312 ObjectInputStream in = new ObjectInputStream(bis);
313 MarshalledObject stub = (MarshalledObject) in.readObject();
314 server = (Naming) stub.get();
315 s.close();
316
317 // Add it to cache
318 addServer(key, server);
319 serverEnv.put("hostKey", key);
320
321 return server;
322 }
323 catch (IOException e)
324 {
325 if(log.isTraceEnabled())
326 log.trace("Failed to retrieve stub from server " + key, e);
327 NamingException ex = new CommunicationException("Failed to retrieve stub from server " + key);
328 ex.setRootCause(e);
329 throw ex;
330 }
331 catch (Exception e)
332 {
333 if(log.isTraceEnabled())
334 log.trace("Failed to connect server " + key, e);
335 NamingException ex = new CommunicationException("Failed to connect to server " + key);
336 ex.setRootCause(e);
337 throw ex;
338 }
339 }
340
341 /**
342 * Create a SocketFactory based on the JNP_SOCKET_FACTORY property in the
343 * given env. If JNP_SOCKET_FACTORY is not specified default to the
344 * TimedSocketFactory.
345 */
346 static SocketFactory loadSocketFactory(Hashtable serverEnv)
347 throws ClassNotFoundException, IllegalAccessException,
348 InstantiationException, InvocationTargetException
349 {
350 SocketFactory factory = null;
351
352 // Get the socket factory classname
353 String socketFactoryName = (String) serverEnv.get(JNP_SOCKET_FACTORY);
354 if (socketFactoryName == null ||
355 socketFactoryName.equals(TimedSocketFactory.class.getName()))
356 {
357 factory = new TimedSocketFactory(serverEnv);
358 return factory;
359 }
360
361 /* Create the socket factory. Look for a ctor that accepts a
362 Hashtable and if not found use the default ctor.
363 */
364 ClassLoader loader = Thread.currentThread().getContextClassLoader();
365 Class factoryClass = loader.loadClass(socketFactoryName);
366 try
367 {
368 Class[] ctorSig = {Hashtable.class};
369 Constructor ctor = factoryClass.getConstructor(ctorSig);
370 Object[] ctorArgs = {serverEnv};
371 factory = (SocketFactory) ctor.newInstance(ctorArgs);
372 }
373 catch (NoSuchMethodException e)
374 {
375 // Use the default ctor
376 factory = (SocketFactory) factoryClass.newInstance();
377 }
378 return factory;
379 }
380
381 static void removeServer(Hashtable serverEnv)
382 {
383 String host = "localhost";
384 int port = 1099;
385
386 // Locate naming service
387 if (serverEnv.get(Context.PROVIDER_URL) != null)
388 {
389 String providerURL = (String) serverEnv.get(Context.PROVIDER_URL);
390
391 StringTokenizer tokenizer = new StringTokenizer(providerURL, ", ");
392 while (tokenizer.hasMoreElements())
393 {
394 String url = tokenizer.nextToken();
395
396 try
397 {
398 // Parse the url into a host:port form, stripping any protocol
399 Name urlAsName = new NamingParser().parse(url);
400 String server = parseNameForScheme(urlAsName, null);
401 if (server != null)
402 url = server;
403
404 Object[] hostAndPort = {url, 1099};
405 parseHostPort(url, hostAndPort, 1099);
406 host = (String) hostAndPort[HOST_INDEX];
407 port = (Integer) hostAndPort[PORT_INDEX];
408
409 // Remove server from map
410 synchronized (NamingContext.class)
411 {
412 InetSocketAddress key = new InetSocketAddress(host, port);
413 cachedServers.remove(key);
414 }
415 }
416 catch (NamingException ignored)
417 {
418 }
419 }
420 }
421
422 // JBAS-4622. Always do this.
423 Object hostKey = serverEnv.remove("hostKey");
424 if (hostKey != null)
425 {
426 synchronized (NamingContext.class)
427 {
428 cachedServers.remove(hostKey);
429 }
430 }
431 }
432
433 /**
434 * Called to remove any url scheme atoms and extract the naming service
435 * hostname:port information.
436 * @param n the name component to the parsed. After returning n will have all
437 * scheme related atoms removed.
438 * @return the naming service hostname:port information string if name
439 * contained the host information.
440 */
441 static String parseNameForScheme(Name n, Hashtable nameEnv)
442 throws InvalidNameException
443 {
444 String serverInfo = null;
445 if (n.size() > 0)
446 {
447 String scheme = n.get(0);
448 int schemeLength = 0;
449 if (scheme.startsWith("java:"))
450 schemeLength = 5;
451 else if (scheme.startsWith("jnp:"))
452 schemeLength = 4;
453 else if (scheme.startsWith("jnps:"))
454 schemeLength = 5;
455 else if (scheme.startsWith("jnp-http:"))
456 schemeLength = 9;
457 else if (scheme.startsWith("jnp-https:"))
458 schemeLength = 10;
459 if (schemeLength > 0)
460 {
461 // Make a copy of the name to avoid
462 n = (Name) n.clone();
463 String suffix = scheme.substring(schemeLength);
464 if (suffix.length() == 0)
465 {
466 // Scheme was "url:/..."
467 n.remove(0);
468 if (n.size() > 1 && n.get(0).equals(""))
469 {
470 // Scheme was "url://hostname:port/..."
471 // Get hostname:port value for the naming server
472 serverInfo = n.get(1);
473 n.remove(0);
474 n.remove(0);
475 // If n is a empty atom remove it or else a '/' will result
476 if (n.size() == 1 && n.get(0).length() == 0)
477 n.remove(0);
478 }
479 }
480 else
481 {
482 // Scheme was "url:foo" -> reinsert "foo"
483 n.remove(0);
484 n.add(0, suffix);
485 }
486 if (nameEnv != null)
487 nameEnv.put(JNP_PARSED_NAME, n);
488 }
489 }
490 return serverInfo;
491 }
492
493 public static Naming getLocal()
494 {
495 SecurityManager security = System.getSecurityManager();
496 if(security != null)
497 security.checkPermission(GET_LOCAL_SERVER);
498 return localServer;
499 }
500 public static void setLocal(Naming server)
501 {
502 SecurityManager security = System.getSecurityManager();
503 if(security != null)
504 security.checkPermission(SET_LOCAL_SERVER);
505 localServer = server;
506 }
507
508 // Constructors --------------------------------------------------
509 public NamingContext(Hashtable e, Name baseName, Naming server)
510 throws NamingException
511 {
512 if (baseName == null)
513 this.prefix = parser.parse("");
514 else
515 this.prefix = baseName;
516
517 if (e != null)
518 this.env = (Hashtable) e.clone();
519 else
520 this.env = new Hashtable();
521
522 this.naming = server;
523 }
524
525 // Public --------------------------------------------------------
526 public Naming getNaming()
527 {
528 return this.naming;
529 }
530
531 public void setNaming(Naming server)
532 {
533 this.naming = server;
534 }
535
536 // Context implementation ----------------------------------------
537 public void rebind(String name, Object obj)
538 throws NamingException
539 {
540 rebind(getNameParser(name).parse(name), obj);
541 }
542
543 public void rebind(Name name, Object obj)
544 throws NamingException
545 {
546 Hashtable refEnv = getEnv(name);
547 checkRef(refEnv);
548 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
549 if (parsedName != null)
550 name = parsedName;
551
552 // Allow state factories to change the stored object
553 obj = getStateToBind(obj, name, refEnv);
554
555 try
556 {
557 String className = null;
558
559 // Referenceable
560 if (obj instanceof Referenceable)
561 obj = ((Referenceable) obj).getReference();
562
563 if (!(obj instanceof Reference))
564 {
565 if( obj != null )
566 className = obj.getClass().getName();
567 obj = createMarshalledValuePair(obj);
568 }
569 else
570 {
571 className = ((Reference) obj).getClassName();
572 }
573 try
574 {
575 naming.rebind(getAbsoluteName(name), obj, className);
576 }
577 catch (RemoteException re)
578 {
579 // Check for JBAS-4574.
580 if (handleStaleNamingStub(re, refEnv))
581 {
582 // try again with new naming stub
583 naming.rebind(getAbsoluteName(name), obj, className);
584 }
585 else
586 {
587 // Not JBAS-4574. Throw exception and let outer logic handle it.
588 throw re;
589 }
590 }
591 }
592 catch (CannotProceedException cpe)
593 {
594 cpe.setEnvironment(refEnv);
595 Context cctx = NamingManager.getContinuationContext(cpe);
596 cctx.rebind(cpe.getRemainingName(), obj);
597 }
598 catch (IOException e)
599 {
600 naming = null;
601 removeServer(refEnv);
602 NamingException ex = new CommunicationException();
603 ex.setRootCause(e);
604 throw ex;
605 }
606 }
607
608 public void bind(String name, Object obj)
609 throws NamingException
610 {
611 bind(getNameParser(name).parse(name), obj);
612 }
613
614 public void bind(Name name, Object obj)
615 throws NamingException
616 {
617 Hashtable refEnv = getEnv(name);
618 checkRef(refEnv);
619 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
620 if (parsedName != null)
621 name = parsedName;
622
623 // Allow state factories to change the stored object
624 obj = getStateToBind(obj, name, refEnv);
625
626 try
627 {
628 String className = null;
629
630 // Referenceable
631 if (obj instanceof Referenceable)
632 obj = ((Referenceable) obj).getReference();
633
634 if (!(obj instanceof Reference))
635 {
636 if( obj != null )
637 className = obj.getClass().getName();
638
639 // Normal object - serialize using a MarshalledValuePair
640 obj = createMarshalledValuePair(obj);
641 }
642 else
643 {
644 className = ((Reference) obj).getClassName();
645 }
646 name = getAbsoluteName(name);
647
648 try
649 {
650 naming.bind(name, obj, className);
651 }
652 catch (RemoteException re)
653 {
654 // Check for JBAS-4574.
655 if (handleStaleNamingStub(re, refEnv))
656 {
657 // try again with new naming stub
658 naming.bind(name, obj, className);
659 }
660 else
661 {
662 // Not JBAS-4574. Throw exception and let outer logic handle it.
663 throw re;
664 }
665 }
666 }
667 catch (CannotProceedException cpe)
668 {
669 cpe.setEnvironment(refEnv);
670 Context cctx = NamingManager.getContinuationContext(cpe);
671 cctx.bind(cpe.getRemainingName(), obj);
672 }
673 catch (IOException e)
674 {
675 naming = null;
676 removeServer(refEnv);
677 NamingException ex = new CommunicationException();
678 ex.setRootCause(e);
679 throw ex;
680 }
681 }
682
683 public Object lookup(String name)
684 throws NamingException
685 {
686 return lookup(getNameParser(name).parse(name));
687 }
688
689 public Object lookup(Name name)
690 throws NamingException
691 {
692 Hashtable refEnv = getEnv(name);
693 checkRef(refEnv);
694 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
695 if (parsedName != null)
696 name = parsedName;
697
698 // Empty?
699 if (name.isEmpty())
700 return new NamingContext(refEnv, prefix, naming);
701
702 try
703 {
704 int maxTries = 1;
705 try
706 {
707 String n = (String) refEnv.get(JNP_MAX_RETRIES);
708 if( n != null )
709 maxTries = Integer.parseInt(n);
710 if( maxTries <= 0 )
711 maxTries = 1;
712 }
713 catch(Exception e)
714 {
715 log.debug("Failed to get JNP_MAX_RETRIES, using 1", e);
716 }
717 Name n = getAbsoluteName(name);
718 Object res = null;
719 boolean trace = log.isTraceEnabled();
720 for (int i = 0; i < maxTries; i++)
721 {
722 try
723 {
724 try
725 {
726 res = naming.lookup(n);
727 }
728 catch (RemoteException re)
729 {
730 // Check for JBAS-4574.
731 if (handleStaleNamingStub(re, refEnv))
732 {
733 // try again with new naming stub
734 res = naming.lookup(n);
735 }
736 else
737 {
738 // Not JBAS-4574. Throw exception and let outer logic handle it.
739 throw re;
740 }
741 }
742 // If we got here, we succeeded, so break the loop
743 break;
744 }
745 catch (ConnectException ce)
746 {
747 int retries = maxTries - i - 1;
748 if( trace )
749 log.trace("Connect failed, retry count: "+retries, ce);
750 // We may overload server so sleep and retry
751 if (retries > 0)
752 {
753 try
754 {
755 Thread.sleep(1);
756 }
757 catch (InterruptedException ignored)
758 {
759 }
760 continue;
761 }
762 // Throw the exception to flush the bad server
763 throw ce;
764 }
765 }
766 if (res instanceof MarshalledValuePair)
767 {
768 MarshalledValuePair mvp = (MarshalledValuePair) res;
769 Object storedObj = mvp.get();
770 return getObjectInstanceWrapFailure(storedObj, name, refEnv);
771 }
772 else if (res instanceof MarshalledObject)
773 {
774 MarshalledObject mo = (MarshalledObject) res;
775 return mo.get();
776 }
777 else if (res instanceof Context)
778 {
779 // Add env
780 Enumeration keys = refEnv.keys();
781 while (keys.hasMoreElements())
782 {
783 String key = (String) keys.nextElement();
784 ((Context) res).addToEnvironment(key, refEnv.get(key));
785 }
786 return res;
787 }
788 else if (res instanceof ResolveResult)
789 {
790 // Dereference partial result
791 ResolveResult rr = (ResolveResult) res;
792 Object resolveRes = rr.getResolvedObj();
793 Object context;
794 Object instanceID;
795
796 if (resolveRes instanceof LinkRef)
797 {
798 context = resolveLink(resolveRes, null);
799 instanceID = ((LinkRef) resolveRes).getLinkName();
800 }
801 else
802 {
803 context = getObjectInstanceWrapFailure(resolveRes, name, refEnv);
804 instanceID = context;
805 }
806
807 if ((context instanceof Context) == false)
808 {
809 throw new NotContextException(instanceID + " is not a Context");
810 }
811 Context ncontext = (Context) context;
812 return ncontext.lookup(rr.getRemainingName());
813 }
814 else if (res instanceof LinkRef)
815 {
816 // Dereference link
817 res = resolveLink(res, refEnv);
818 }
819 else if (res instanceof Reference)
820 {
821 // Dereference object
822 res = getObjectInstanceWrapFailure(res, name, refEnv);
823 if (res instanceof LinkRef)
824 res = resolveLink(res, refEnv);
825 }
826
827 return res;
828 }
829 catch (CannotProceedException cpe)
830 {
831 cpe.setEnvironment(refEnv);
832 Context cctx = NamingManager.getContinuationContext(cpe);
833 return cctx.lookup(cpe.getRemainingName());
834 }
835 catch (IOException e)
836 {
837 naming = null;
838 removeServer(refEnv);
839 NamingException ex = new CommunicationException();
840 ex.setRootCause(e);
841 throw ex;
842 }
843 catch (ClassNotFoundException e)
844 {
845 NamingException ex = new CommunicationException();
846 ex.setRootCause(e);
847 throw ex;
848 }
849 }
850
851 public void unbind(String name)
852 throws NamingException
853 {
854 unbind(getNameParser(name).parse(name));
855 }
856
857
858 public void unbind(Name name)
859 throws NamingException
860 {
861 Hashtable refEnv = getEnv(name);
862 checkRef(refEnv);
863 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
864 if (parsedName != null)
865 name = parsedName;
866
867 try
868 {
869 try
870 {
871 naming.unbind(getAbsoluteName(name));
872 }
873 catch (RemoteException re)
874 {
875 // Check for JBAS-4574.
876 if (handleStaleNamingStub(re, refEnv))
877 {
878 // try again with new naming stub
879 naming.unbind(getAbsoluteName(name));
880 }
881 else
882 {
883 // Not JBAS-4574. Throw exception and let outer logic handle it.
884 throw re;
885 }
886 }
887 }
888 catch (CannotProceedException cpe)
889 {
890 cpe.setEnvironment(refEnv);
891 Context cctx = NamingManager.getContinuationContext(cpe);
892 cctx.unbind(cpe.getRemainingName());
893 }
894 catch (IOException e)
895 {
896 naming = null;
897 removeServer(refEnv);
898 NamingException ex = new CommunicationException();
899 ex.setRootCause(e);
900 throw ex;
901 }
902 }
903
904 public void rename(String oldname, String newname)
905 throws NamingException
906 {
907 rename(getNameParser(oldname).parse(oldname), getNameParser(newname).parse(newname));
908 }
909
910 public void rename(Name oldName, Name newName)
911 throws NamingException
912 {
913 bind(newName, lookup(oldName));
914 unbind(oldName);
915 }
916
917 public NamingEnumeration list(String name)
918 throws NamingException
919 {
920 return list(getNameParser(name).parse(name));
921 }
922
923 public NamingEnumeration list(Name name)
924 throws NamingException
925 {
926 Hashtable refEnv = getEnv(name);
927 checkRef(refEnv);
928 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
929 if (parsedName != null)
930 name = parsedName;
931
932 try
933 {
934 Collection c = null;
935 try
936 {
937 c = naming.list(getAbsoluteName(name));
938 }
939 catch (RemoteException re)
940 {
941 // Check for JBAS-4574.
942 if (handleStaleNamingStub(re, refEnv))
943 {
944 // try again with new naming stub
945 c = naming.list(getAbsoluteName(name));
946 }
947 else
948 {
949 // Not JBAS-4574. Throw exception and let outer logic handle it.
950 throw re;
951 }
952 }
953 return new NamingEnumerationImpl(c);
954 }
955 catch (CannotProceedException cpe)
956 {
957 cpe.setEnvironment(refEnv);
958 Context cctx = NamingManager.getContinuationContext(cpe);
959 return cctx.list(cpe.getRemainingName());
960 }
961 catch (IOException e)
962 {
963 naming = null;
964 removeServer(refEnv);
965 NamingException ex = new CommunicationException();
966 ex.setRootCause(e);
967 throw ex;
968 }
969 }
970
971 public NamingEnumeration listBindings(String name)
972 throws NamingException
973 {
974 return listBindings(getNameParser(name).parse(name));
975 }
976
977 public NamingEnumeration listBindings(Name name)
978 throws NamingException
979 {
980 Hashtable refEnv = getEnv(name);
981 checkRef(refEnv);
982 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
983 if (parsedName != null)
984 name = parsedName;
985
986 try
987 {
988 // Get list
989 Collection bindings = null;
990 try
991 {
992 // Get list
993 bindings = naming.listBindings(getAbsoluteName(name));
994 }
995 catch (RemoteException re)
996 {
997 // Check for JBAS-4574.
998 if (handleStaleNamingStub(re, refEnv))
999 {
1000 // try again with new naming stub
1001 bindings = naming.listBindings(getAbsoluteName(name));
1002 }
1003 else
1004 {
1005 // Not JBAS-4574. Throw exception and let outer logic handle it.
1006 throw re;
1007 }
1008 }
1009 Collection realBindings = new ArrayList(bindings.size());
1010
1011 // Convert marshalled objects
1012 Iterator i = bindings.iterator();
1013 while (i.hasNext())
1014 {
1015 Binding binding = (Binding) i.next();
1016 Object obj = binding.getObject();
1017 if (obj instanceof MarshalledValuePair)
1018 {
1019 try
1020 {
1021 obj = ((MarshalledValuePair) obj).get();
1022 }
1023 catch (ClassNotFoundException e)
1024 {
1025 NamingException ex = new CommunicationException();
1026 ex.setRootCause(e);
1027 throw ex;
1028 }
1029 }
1030 else if (obj instanceof MarshalledObject)
1031 {
1032 try
1033 {
1034 obj = ((MarshalledObject) obj).get();
1035 }
1036 catch (ClassNotFoundException e)
1037 {
1038 NamingException ex = new CommunicationException();
1039 ex.setRootCause(e);
1040 throw ex;
1041 }
1042 }
1043 realBindings.add(new Binding(binding.getName(), binding.getClassName(), obj));
1044 }
1045
1046 // Return transformed list of bindings
1047 return new NamingEnumerationImpl(realBindings);
1048 }
1049 catch (CannotProceedException cpe)
1050 {
1051 cpe.setEnvironment(refEnv);
1052 Context cctx = NamingManager.getContinuationContext(cpe);
1053 return cctx.listBindings(cpe.getRemainingName());
1054 }
1055 catch (IOException e)
1056 {
1057 naming = null;
1058 removeServer(refEnv);
1059 NamingException ex = new CommunicationException();
1060 ex.setRootCause(e);
1061 throw ex;
1062 }
1063 }
1064
1065 public String composeName(String name, String prefix)
1066 throws NamingException
1067 {
1068 Name result = composeName(parser.parse(name),
1069 parser.parse(prefix));
1070 return result.toString();
1071 }
1072
1073 public Name composeName(Name name, Name prefix)
1074 throws NamingException
1075 {
1076 Name result = (Name) (prefix.clone());
1077 result.addAll(name);
1078 return result;
1079 }
1080
1081 public NameParser getNameParser(String name)
1082 throws NamingException
1083 {
1084 return parser;
1085 }
1086
1087 public NameParser getNameParser(Name name)
1088 throws NamingException
1089 {
1090 return getNameParser(name.toString());
1091 }
1092
1093 public Context createSubcontext(String name)
1094 throws NamingException
1095 {
1096 return createSubcontext(getNameParser(name).parse(name));
1097 }
1098
1099 public Context createSubcontext(Name name)
1100 throws NamingException
1101 {
1102 if (name.size() == 0)
1103 throw new InvalidNameException("Cannot pass an empty name to createSubcontext");
1104
1105 Hashtable refEnv = getEnv(name);
1106 checkRef(refEnv);
1107 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
1108 if (parsedName != null)
1109 name = parsedName;
1110
1111 try
1112 {
1113 name = getAbsoluteName(name);
1114 try
1115 {
1116 return naming.createSubcontext(name);
1117 }
1118 catch (RemoteException re)
1119 {
1120 // Check for JBAS-4574.
1121 if (handleStaleNamingStub(re, refEnv))
1122 {
1123 // try again with new naming stub
1124 return naming.createSubcontext(name);
1125 }
1126 else
1127 {
1128 // Not JBAS-4574. Throw exception and let outer logic handle it.
1129 throw re;
1130 }
1131 }
1132 }
1133 catch (CannotProceedException cpe)
1134 {
1135 cpe.setEnvironment(refEnv);
1136 Context cctx = NamingManager.getContinuationContext(cpe);
1137 return cctx.createSubcontext(cpe.getRemainingName());
1138 }
1139 catch (IOException e)
1140 {
1141 naming = null;
1142 removeServer(refEnv);
1143 NamingException ex = new CommunicationException();
1144 ex.setRootCause(e);
1145 throw ex;
1146 }
1147 }
1148
1149 public Object addToEnvironment(String propName, Object propVal)
1150 throws NamingException
1151 {
1152 Object old = env.get(propName);
1153 env.put(propName, propVal);
1154 return old;
1155 }
1156
1157 public Object removeFromEnvironment(String propName)
1158 throws NamingException
1159 {
1160 return env.remove(propName);
1161 }
1162
1163 public Hashtable getEnvironment()
1164 throws NamingException
1165 {
1166 return env;
1167 }
1168
1169 public void close()
1170 throws NamingException
1171 {
1172 env = null;
1173 naming = null;
1174 }
1175
1176 public String getNameInNamespace()
1177 throws NamingException
1178 {
1179 return prefix.toString();
1180 }
1181
1182 public void destroySubcontext(String name)
1183 throws NamingException
1184 {
1185 destroySubcontext(getNameParser(name).parse(name));
1186 }
1187
1188 public void destroySubcontext(Name name)
1189 throws NamingException
1190 {
1191 if (!list(name).hasMore())
1192 {
1193 unbind(name);
1194 }
1195 else
1196 throw new ContextNotEmptyException();
1197 }
1198
1199 public Object lookupLink(String name)
1200 throws NamingException
1201 {
1202 return lookupLink(getNameParser(name).parse(name));
1203 }
1204
1205 /**
1206 * Lookup the object referred to by name but don't dereferrence the final
1207 * component. This really just involves returning the raw value returned by
1208 * the Naming.lookup() method.
1209 * @return the raw object bound under name.
1210 */
1211 public Object lookupLink(Name name)
1212 throws NamingException
1213 {
1214 Hashtable refEnv = getEnv(name);
1215 checkRef(refEnv);
1216 Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
1217 if (parsedName != null)
1218 name = parsedName;
1219
1220 if (name.isEmpty())
1221 return lookup(name);
1222
1223 Object link = null;
1224 try
1225 {
1226 Name n = getAbsoluteName(name);
1227 try
1228 {
1229 link = naming.lookup(n);
1230 }
1231 catch (RemoteException re)
1232 {
1233 // Check for JBAS-4574.
1234 if (handleStaleNamingStub(re, refEnv))
1235 {
1236 // try again with new naming stub
1237 link = naming.lookup(n);
1238 }
1239 else
1240 {
1241 // Not JBAS-4574. Throw exception and let outer logic handle it.
1242 throw re;
1243 }
1244 }
1245 if (!(link instanceof LinkRef) && link instanceof Reference)
1246 link = getObjectInstance(link, name, null);
1247 ;
1248 }
1249 catch (IOException e)
1250 {
1251 naming = null;
1252 removeServer(refEnv);
1253 NamingException ex = new CommunicationException();
1254 ex.setRootCause(e);
1255 throw ex;
1256 }
1257 catch (Exception e)
1258 {
1259 NamingException ex = new NamingException("Could not lookup link");
1260 ex.setRemainingName(name);
1261 ex.setRootCause(e);
1262 throw ex;
1263 }
1264 return link;
1265 }
1266
1267 // Begin EventContext methods
1268 public void addNamingListener(Name target, int scope, NamingListener l)
1269 throws NamingException
1270 {
1271 if((naming instanceof NamingEvents) == false)
1272 {
1273 throw new UnsupportedOperationException("Naming implementation does not support NamingExt");
1274 }
1275 NamingEvents next = (NamingEvents) naming;
1276 try
1277 {
1278 next.addNamingListener(this, target, scope, l);
1279 }
1280 catch (RemoteException e)
1281 {
1282 CommunicationException ce = new CommunicationException("addNamingListener failed");
1283 ce.initCause(e);
1284 }
1285 }
1286
1287 public void addNamingListener(String target, int scope, NamingListener l)
1288 throws NamingException
1289 {
1290 Name targetName = parser.parse(target);
1291 addNamingListener(targetName, scope, l);
1292 }
1293
1294 public void removeNamingListener(NamingListener l)
1295 throws NamingException
1296 {
1297 if((naming instanceof NamingEvents) == false)
1298 {
1299 throw new UnsupportedOperationException("Naming implementation does not support NamingExt");
1300 }
1301 NamingEvents next = (NamingEvents) naming;
1302 try
1303 {
1304 next.removeNamingListener(l);
1305 }
1306 catch (RemoteException e)
1307 {
1308 CommunicationException ce = new CommunicationException("removeNamingListener failed");
1309 ce.initCause(e);
1310 }
1311 }
1312
1313 public boolean targetMustExist()
1314 throws NamingException
1315 {
1316 if((naming instanceof NamingEvents) == false)
1317 {
1318 throw new UnsupportedOperationException("Naming implementation does not support NamingExt");
1319 }
1320 NamingEvents next = (NamingEvents) naming;
1321 boolean targetMustExist = true;
1322 try
1323 {
1324 targetMustExist = next.targetMustExist();
1325 }
1326 catch (RemoteException e)
1327 {
1328 CommunicationException ce = new CommunicationException("removeNamingListener failed");
1329 ce.initCause(e);
1330 }
1331 return targetMustExist;
1332 }
1333 // End EventContext methods
1334
1335 protected Object resolveLink(Object res, Hashtable refEnv)
1336 throws NamingException
1337 {
1338 Object linkResult = null;
1339 try
1340 {
1341 LinkRef link = (LinkRef) res;
1342 String ref = link.getLinkName();
1343 if (ref.startsWith("./"))
1344 linkResult = lookup(ref.substring(2));
1345 else if (refEnv != null)
1346 linkResult = new InitialContext(refEnv).lookup(ref);
1347 else
1348 linkResult = new InitialContext().lookup(ref);
1349 }
1350 catch (Exception e)
1351 {
1352 NamingException ex = new NamingException("Could not dereference object");
1353 ex.setRootCause(e);
1354 throw ex;
1355 }
1356 return linkResult;
1357 }
1358
1359 protected boolean shouldDiscoveryHappen(boolean globalDisableDiscovery, String perCtxDisableDiscovery)
1360 {
1361 boolean trace = log.isTraceEnabled();
1362 if (!globalDisableDiscovery)
1363 {
1364 // No global disable, so act as before.
1365 if (Boolean.valueOf(perCtxDisableDiscovery) == Boolean.TRUE)
1366 {
1367 if (trace)
1368 log.trace("Skipping discovery due to disable flag in context");
1369 return false;
1370 }
1371 }
1372 else
1373 {
1374 // Global disable on but double check whether there's a per context override.
1375 // If disableDiscovery in context is explicitly set to false, do discovery.
1376 if (perCtxDisableDiscovery == null || Boolean.valueOf(perCtxDisableDiscovery) == Boolean.TRUE)
1377 {
1378 if (trace)
1379 log.trace("Skipping discovery due to disable flag in context, or disable flag globally (and no override in context)");
1380 return false;
1381 }
1382 }
1383
1384 return true;
1385 }
1386
1387 // Private -------------------------------------------------------
1388
1389 /**
1390 * Isolate the creation of the MarshalledValuePair in a privileged block
1391 * when running under a security manager so the following permissions can
1392 * be isolated from the caller:
1393 * RuntimePermission("createClassLoader")
1394 ReflectPermission("suppressAccessChecks")
1395 SerializablePermission("enableSubstitution")
1396 @return the MarshalledValuePair wrapping obj
1397 */
1398 private Object createMarshalledValuePair(final Object obj)
1399 throws IOException
1400 {
1401 MarshalledValuePair mvp = null;
1402 SecurityManager sm = System.getSecurityManager();
1403 if(sm != null)
1404 {
1405 try
1406 {
1407 mvp = AccessController.doPrivileged(new PrivilegedExceptionAction<MarshalledValuePair>()
1408 {
1409 public MarshalledValuePair run() throws Exception
1410 {
1411 return new MarshalledValuePair(obj);
1412 }
1413 }
1414 );
1415 }
1416 catch(PrivilegedActionException e)
1417 {
1418 IOException ioe = new IOException();
1419 ioe.initCause(e.getException());
1420 throw ioe;
1421 }
1422 }
1423 else
1424 {
1425 mvp = new MarshalledValuePair(obj);
1426 }
1427 return mvp;
1428 }
1429
1430 /**
1431 * Determine the form of the name to pass to the NamingManager operations.
1432 * This is supposed to be a context relative name according to the javaodcs
1433 * for NamingManager, but historically the absolute name of the target
1434 * context has been passed in.
1435 *
1436 * @param env - the env of NamingContext that op was called on
1437 * @return true if the legacy and technically incorrect absolute name should
1438 * be used, false if the context relative name should be used.
1439 */
1440 private boolean useAbsoluteName(Hashtable env)
1441 {
1442 if (env == null)
1443 return true;
1444 String useRelativeName = (String) env.get(JNP_USE_RELATIVE_NAME);
1445 return Boolean.valueOf(useRelativeName) == Boolean.FALSE;
1446 }
1447
1448 /**
1449 * Use the NamingManager.getStateToBind to obtain the actual object to bind
1450 * into jndi.
1451 * @param obj - the value passed to bind/rebind
1452 * @param name - the name passed to bind/rebind
1453 * @param env - the env of NamingContext that bind/rebind was called on
1454 * @return the object to bind to the naming server
1455 * @throws NamingException
1456 */
1457 private Object getStateToBind(Object obj, Name name, Hashtable env)
1458 throws NamingException
1459 {
1460 if (useAbsoluteName(env))
1461 name = getAbsoluteName(name);
1462 return NamingManager.getStateToBind(obj, name, this, env);
1463 }
1464
1465 /**
1466 * Use the NamingManager.getObjectInstance to resolve the raw object obtained
1467 * from the naming server.
1468 * @param obj - raw value obtained from the naming server
1469 * @param name - the name passed to the lookup op
1470 * @param env - the env of NamingContext that the op was called on
1471 * @return the fully resolved object
1472 * @throws Exception
1473 */
1474 private Object getObjectInstance(Object obj, Name name, Hashtable env)
1475 throws Exception
1476 {
1477 if (useAbsoluteName(env))
1478 name = getAbsoluteName(name);
1479 return NamingManager.getObjectInstance(obj, name, this, env);
1480 }
1481
1482 /**
1483 * Resolve the final object and wrap any non-NamingException errors in a
1484 * NamingException with the cause passed as the root cause.
1485 * @param obj - raw value obtained from the naming server
1486 * @param name - the name passed to the lookup op
1487 * @param env - the env of NamingContext that the op was called on
1488 * @return the fully resolved object
1489 * @throws NamingException
1490 */
1491 private Object getObjectInstanceWrapFailure(Object obj, Name name, Hashtable env)
1492 throws NamingException
1493 {
1494 try
1495 {
1496 return getObjectInstance(obj, name, env);
1497 }
1498 catch (NamingException e)
1499 {
1500 throw e;
1501 }
1502 catch (Exception e)
1503 {
1504 NamingException ex = new NamingException("Could not dereference object");
1505 ex.setRootCause(e);
1506 throw ex;
1507 }
1508 }
1509
1510 /**
1511 * This methods sends a broadcast message on the network and asks and HA-JNDI
1512 * server to sent it the HA-JNDI stub
1513 */
1514 private Naming discoverServer(Hashtable serverEnv) throws NamingException
1515 {
1516 boolean trace = log.isTraceEnabled();
1517 // Check if discovery should be done
1518 String disableDiscovery = (String) serverEnv.get(JNP_DISABLE_DISCOVERY);
1519
1520 if (!shouldDiscoveryHappen(GLOBAL_JNP_DISABLE_DISCOVERY, disableDiscovery))
1521 {
1522 return null;
1523 }
1524
1525 if (Boolean.valueOf(disableDiscovery) == Boolean.TRUE)
1526 {
1527 if (trace)
1528 log.trace("Skipping discovery due to disable flag");
1529 return null;
1530 }
1531
1532 // we first try to discover the server locally
1533 //
1534 String partitionName = (String) serverEnv.get(JNP_PARTITION_NAME);
1535 Naming server = null;
1536 if (partitionName != null)
1537 {
1538 server = getHANamingServerForPartition(partitionName);
1539 if (server != null)
1540 return server;
1541 }
1542
1543 // We next broadcast a HelloWorld datagram (multicast)
1544 // Any listening server will answer with its IP address:port in another datagram
1545 // we will then use this to make a standard "lookup"
1546 //
1547 MulticastSocket s = null;
1548 InetAddress iaGroup = null;
1549 try
1550 {
1551 String group = DEFAULT_DISCOVERY_GROUP_ADDRESS;
1552 int port = DEFAULT_DISCOVERY_GROUP_PORT;
1553 int timeout = DEFAULT_DISCOVERY_TIMEOUT;
1554 int ttl = 16;
1555
1556 String discoveryGroup = (String) serverEnv.get(JNP_DISCOVERY_GROUP);
1557 if (discoveryGroup != null)
1558 group = discoveryGroup;
1559
1560 String discoveryTTL = (String) serverEnv.get(JNP_DISCOVERY_TTL);
1561 if(discoveryTTL != null)
1562 ttl = Integer.parseInt(discoveryTTL);
1563
1564 String discoveryTimeout = (String) serverEnv.get(JNP_DISCOVERY_TIMEOUT);
1565 if (discoveryTimeout == null)
1566 {
1567 // Check the old property name
1568 discoveryTimeout = (String) serverEnv.get("DISCOVERY_TIMEOUT");
1569 }
1570 if (discoveryTimeout != null && !discoveryTimeout.equals(""))
1571 timeout = Integer.parseInt(discoveryTimeout);
1572
1573 String discoveryGroupPort = (String) serverEnv.get(JNP_DISCOVERY_PORT);
1574 if (discoveryGroupPort == null)
1575 {
1576 // Check the old property name
1577 discoveryGroupPort = (String) serverEnv.get("DISCOVERY_GROUP");
1578 }
1579 if (discoveryGroupPort != null && !discoveryGroupPort.equals(""))
1580 {
1581 int colon = discoveryGroupPort.indexOf(':');
1582 if (colon < 0)
1583 {
1584 // No group given, just the port
1585 try
1586 {
1587 port = Integer.parseInt(discoveryGroupPort);
1588 }
1589 catch (Exception ex)
1590 {
1591 log.warn("Failed to parse port: " + discoveryGroupPort, ex);
1592 }
1593 }
1594 else
1595 {
1596 // The old group:port syntax was given
1597 group = discoveryGroupPort.substring(0, colon);
1598 String portStr = discoveryGroupPort.substring(colon + 1);
1599 try
1600 {
1601 port = Integer.parseInt(portStr);
1602 }
1603 catch (Exception ex)
1604 {
1605 log.warn("Failed to parse port: " + portStr, ex);
1606 }
1607 }
1608 }
1609
1610 iaGroup = InetAddress.getByName(group);
1611 String localAddrStr = (String) serverEnv.get(JNP_LOCAL_ADDRESS);
1612 String localPortStr = (String) serverEnv.get(JNP_LOCAL_PORT);
1613 int localPort = 0;
1614 if (localPortStr != null)
1615 localPort = Integer.parseInt(localPortStr);
1616 if (localAddrStr != null)
1617 {
1618 InetSocketAddress localAddr = new InetSocketAddress(localAddrStr, localPort);
1619 s = new MulticastSocket(localAddr);
1620 }
1621 else
1622 {
1623 s = new MulticastSocket(localPort);
1624 }
1625 s.setSoTimeout(timeout);
1626 s.setTimeToLive(ttl);
1627 if(log.isTraceEnabled())
1628 log.trace("TTL on multicast discovery socket is " + ttl);
1629 s.joinGroup(iaGroup);
1630 if (trace)
1631 log.trace("MulticastSocket: " + s);
1632 DatagramPacket packet;
1633 // Send a request optionally restricted to a cluster partition
1634 StringBuffer data = new StringBuffer("GET_ADDRESS");
1635 if (partitionName != null)
1636 data.append(":" + partitionName);
1637 byte[] buf = data.toString().getBytes();
1638 packet = new DatagramPacket(buf, buf.length, iaGroup, port);
1639 if (trace)
1640 log.trace("Sending discovery packet(" + data + ") to: " + iaGroup + ":" + port);
1641 s.send(packet);
1642 // Look for a reply
1643 // IP address + port number = 128.128.128.128:65535 => (12+3) + 1 + (5) = 21
1644
1645 buf = new byte[50];
1646 packet = new DatagramPacket(buf, buf.length);
1647 s.receive(packet);
1648 String myServer = new String(packet.getData()).trim();
1649 if (trace)
1650 log.trace("Received answer packet: " + myServer);
1651 while (myServer != null && myServer.startsWith("GET_ADDRESS"))
1652 {
1653 Arrays.fill(buf, (byte) 0);
1654 packet.setLength(buf.length);
1655 s.receive(packet);
1656 byte[] reply = packet.getData();
1657 myServer = new String(reply).trim();
1658 if (trace)
1659 log.trace("Received answer packet: " + myServer);
1660 }
1661 String serverHost;
1662 int serverPort;
1663
1664 Object[] hostAndPort = {myServer, 0};
1665 parseHostPort(myServer, hostAndPort, DEFAULT_DISCOVERY_GROUP_PORT);
1666 serverHost = (String) hostAndPort[HOST_INDEX];
1667 serverPort = (Integer) hostAndPort[PORT_INDEX];
1668 if (serverHost != null)
1669 {
1670 server = getServer(serverHost, serverPort, serverEnv);
1671 }
1672 return server;
1673 }
1674 catch (IOException e)
1675 {
1676 if (trace)
1677 log.trace("Discovery failed", e);
1678 NamingException ex = new CommunicationException(e.getMessage());
1679 ex.setRootCause(e);
1680 throw ex;
1681 }
1682 finally
1683 {
1684 try
1685 {
1686 if (s != null)
1687 s.leaveGroup(iaGroup);
1688 }
1689 catch (Exception ignore)
1690 {
1691 }
1692 try
1693 {
1694 if (s != null)
1695 s.close();
1696 }
1697 catch (Exception ignore)
1698 {
1699 }
1700 }
1701 }
1702
1703 private void checkRef(Hashtable refEnv)
1704 throws NamingException
1705 {
1706 if (naming == null)
1707 {
1708 String host = "localhost";
1709 int port = 1099;
1710 Exception serverEx = null;
1711
1712 // Locate first available naming service
1713 String urls = (String) refEnv.get(Context.PROVIDER_URL);
1714 if (urls != null && urls.length() > 0)
1715 {
1716 StringTokenizer tokenizer = new StringTokenizer(urls, ",");
1717
1718 while (naming == null && tokenizer.hasMoreElements())
1719 {
1720 String url = tokenizer.nextToken();
1721 // Parse the url into a host:port form, stripping any protocol
1722 Name urlAsName = getNameParser("").parse(url);
1723 String server = parseNameForScheme(urlAsName, null);
1724 if (server != null)
1725 url = server;
1726 //
1727 Object[] hostAndPort = {url, 0};
1728 parseHostPort(url, hostAndPort, 1099);
1729 host = (String) hostAndPort[HOST_INDEX];
1730 port = (Integer) hostAndPort[PORT_INDEX];
1731 try
1732 {
1733 // Get server from cache
1734 naming = getServer(host, port, refEnv);
1735 }
1736 catch (Exception e)
1737 {
1738 serverEx = e;
1739 log.debug("Failed to connect to " + host + ":" + port, e);
1740 }
1741 }
1742
1743 // If there is still no
1744 Exception discoveryFailure = null;
1745 if (naming == null)
1746 {
1747 try
1748 {
1749 naming = discoverServer(refEnv);
1750 }
1751 catch (Exception e)
1752 {
1753 discoveryFailure = e;
1754 if (serverEx == null)
1755 serverEx = e;
1756 }
1757 if (naming == null)
1758 {
1759 StringBuffer buffer = new StringBuffer(50);
1760 buffer.append("Could not obtain connection to any of these urls: ").append(urls);
1761 if (discoveryFailure != null)
1762 buffer.append(" and discovery failed with error: ").append(discoveryFailure);
1763 CommunicationException ce = new CommunicationException(buffer.toString());
1764 ce.setRootCause(serverEx);
1765 throw ce;
1766 }
1767 }
1768 }
1769 else
1770 {
1771 // If we are in a clustering scenario, the client code may request a context
1772 // for a *specific* HA-JNDI service (i.e. linked to a *specific* partition)
1773 // EVEN if the lookup is done inside a JBoss VM. For example, a JBoss service
1774 // may do a lookup on a HA-JNDI service running on another host *without*
1775 // explicitly providing a PROVIDER_URL but simply by providing a JNP_PARTITON_NAME
1776 // parameter so that dynamic discovery can be used
1777 //
1778 String jnpPartitionName = (String) refEnv.get(JNP_PARTITION_NAME);
1779 if (jnpPartitionName != null)
1780 {
1781 // the client is requesting for a specific partition name
1782 //
1783 naming = discoverServer(refEnv);
1784 if (naming == null)
1785 throw new ConfigurationException
1786 ("No valid context could be build for jnp.partitionName=" + jnpPartitionName);
1787 }
1788 else
1789 {
1790 // Use server in same JVM
1791 naming = localServer;
1792
1793 if (naming == null)
1794 {
1795 naming = discoverServer(refEnv);
1796 if (naming == null)
1797 // Local, but no local JNDI provider found!
1798 throw new ConfigurationException("No valid Context.PROVIDER_URL was found");
1799 }
1800 }
1801 }
1802 }
1803 }
1804
1805 /**
1806 * Parse a naming provider url for the host/port information
1807 * @param url - the naming provider url string to parse
1808 * @param output, [0] = the host name/address, [1] = the parsed port as an Integer
1809 * @param defaultPort - the default port to return in output[1] if no port
1810 * was seen in the url string.
1811 * @return the index of the port separator if found, -1 otherwise.
1812 */
1813 static private int parseHostPort(String url, Object[] output, int defaultPort)
1814 {
1815 // First look for a @ separating the host and port
1816 int colon = url.indexOf('@');
1817 if(colon < 0)
1818 {
1819 // If there are multiple ':' assume its an IPv6 address
1820 colon = url.lastIndexOf(':');
1821 int firstColon = url.indexOf(':');
1822 if(colon > firstColon)
1823 colon = -1;
1824 }
1825
1826 if(colon < 0)
1827 {
1828 output[HOST_INDEX] = url;
1829 output[PORT_INDEX] = new Integer(defaultPort);
1830 }
1831 else
1832 {
1833 output[HOST_INDEX] = url.substring(0, colon);
1834 try
1835 {
1836 output[PORT_INDEX] = Integer.parseInt(url.substring(colon+1).trim());
1837 }
1838 catch (Exception ex)
1839 {
1840 // Use default port
1841 output[PORT_INDEX] = new Integer(defaultPort);
1842 }
1843 }
1844 return colon;
1845 }
1846
1847 private Name getAbsoluteName(Name n)
1848 throws NamingException
1849 {
1850 if (n.isEmpty())
1851 return composeName(n, prefix);
1852 else if (n.get(0).toString().equals("")) // Absolute name
1853 return n.getSuffix(1);
1854 else // Add prefix
1855 return composeName(n, prefix);
1856 }
1857
1858 private Hashtable getEnv(Name n)
1859 throws InvalidNameException
1860 {
1861 Hashtable nameEnv = env;
1862 env.remove(JNP_PARSED_NAME);
1863 String serverInfo = parseNameForScheme(n, nameEnv);
1864 if (serverInfo != null)
1865 {
1866 // Set hostname:port value for the naming server
1867 nameEnv = (Hashtable) env.clone();
1868 nameEnv.put(Context.PROVIDER_URL, serverInfo);
1869 }
1870 return nameEnv;
1871 }
1872
1873 /**
1874 * JBAS-4574. Check if the given exception is because the server has
1875 * been restarted while the cached naming stub hasn't been dgc-ed yet.
1876 * If yes, we will flush out the naming stub from our cache and
1877 * acquire a new stub. BW.
1878 *
1879 * @param e the exception that may be due to a stale stub
1880 * @param refEnv the naming environment associated with the failed call
1881 *
1882 * @return <code>true</code> if <code>e</code> indicates a stale
1883 * naming stub and we were able to succesfully flush the
1884 * cache and acquire a new stub; <code>false</code> otherwise.
1885 */
1886 private boolean handleStaleNamingStub(Exception e, Hashtable refEnv)
1887 {
1888 if (e instanceof NoSuchObjectException
1889 || e.getCause() instanceof NoSuchObjectException)
1890 {
1891 try
1892 {
1893 if( log.isTraceEnabled() )
1894 {
1895 log.trace("Call failed with NoSuchObjectException, " +
1896 "flushing server cache and retrying", e);
1897 }
1898 naming = null;
1899 removeServer(refEnv);
1900
1901 checkRef(refEnv);
1902
1903 return true;
1904 }
1905 catch (Exception e1)
1906 {
1907 // Just log and return false; let caller continue processing
1908 // the original exception passed in to this method
1909 log.error("Caught exception flushing server cache and " +
1910 "re-establish naming after exception " +
1911 e.getLocalizedMessage(), e1);
1912 }
1913 }
1914 return false;
1915 }
1916
1917 // Inner classes -------------------------------------------------
1918 }