0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jnp.interfaces;
0023:
0024: import java.io.BufferedInputStream;
0025: import java.io.IOException;
0026: import java.io.ObjectInputStream;
0027: import java.lang.ref.WeakReference;
0028: import java.lang.reflect.Constructor;
0029: import java.lang.reflect.InvocationTargetException;
0030: import java.net.DatagramPacket;
0031: import java.net.InetAddress;
0032: import java.net.MulticastSocket;
0033: import java.net.Socket;
0034: import java.net.InetSocketAddress;
0035: import java.rmi.ConnectException;
0036: import java.rmi.MarshalledObject;
0037: import java.util.ArrayList;
0038: import java.util.Arrays;
0039: import java.util.Collection;
0040: import java.util.Enumeration;
0041: import java.util.HashMap;
0042: import java.util.Hashtable;
0043: import java.util.Iterator;
0044: import java.util.StringTokenizer;
0045: import javax.naming.Binding;
0046: import javax.naming.CannotProceedException;
0047: import javax.naming.CommunicationException;
0048: import javax.naming.ConfigurationException;
0049: import javax.naming.Context;
0050: import javax.naming.InitialContext;
0051: import javax.naming.InvalidNameException;
0052: import javax.naming.LinkRef;
0053: import javax.naming.Name;
0054: import javax.naming.NameParser;
0055: import javax.naming.NamingEnumeration;
0056: import javax.naming.NamingException;
0057: import javax.naming.NotContextException;
0058: import javax.naming.OperationNotSupportedException;
0059: import javax.naming.Reference;
0060: import javax.naming.Referenceable;
0061: import javax.naming.ServiceUnavailableException;
0062: import javax.naming.spi.NamingManager;
0063: import javax.naming.spi.ResolveResult;
0064: import javax.net.SocketFactory;
0065:
0066: import org.jboss.logging.Logger;
0067:
0068: /**
0069: * This class provides the jnp provider Context implementation. It is a Context
0070: * interface wrapper for a RMI Naming instance that is obtained from either the
0071: * local server instance or by locating the server given by the
0072: * Context.PROVIDER_URL value.
0073: *
0074: * This class also serves as the jnp url resolution context. jnp style urls
0075: * passed to the
0076: * @author oberg
0077: * @author scott.stark@jboss.org
0078: * @version $Revision: 63339 $
0079: */
0080: public class NamingContext implements Context, java.io.Serializable {
0081: // Constants -----------------------------------------------------
0082: /**
0083: * @since 1.7
0084: */
0085: static final long serialVersionUID = 8906455608484282128L;
0086: /**
0087: * The javax.net.SocketFactory impl to use for the bootstrap socket
0088: */
0089: public static final String JNP_SOCKET_FACTORY = "jnp.socketFactory";
0090: /**
0091: * The local address to bind the connected bootstrap socket to
0092: */
0093: public static final String JNP_LOCAL_ADDRESS = "jnp.localAddress";
0094: /**
0095: * The local port to bind the connected bootstrap socket to
0096: */
0097: public static final String JNP_LOCAL_PORT = "jnp.localPort";
0098: /**
0099: * A flag to disable the broadcast discovery queries
0100: */
0101: public static final String JNP_DISABLE_DISCOVERY = "jnp.disableDiscovery";
0102: /**
0103: * The cluster partition discovery should be restricted to
0104: */
0105: public static final String JNP_PARTITION_NAME = "jnp.partitionName";
0106: /**
0107: * The multicast IP/address to which the discovery query is sent
0108: */
0109: public static final String JNP_DISCOVERY_GROUP = "jnp.discoveryGroup";
0110: /**
0111: * The port to which the discovery query is sent
0112: */
0113: public static final String JNP_DISCOVERY_PORT = "jnp.discoveryPort";
0114:
0115: /** The time-to-live for the multicast discovery packets */
0116: public static final String JNP_DISCOVERY_TTL = "jnp.discoveryTTL";
0117:
0118: /**
0119: * The time in MS to wait for a discovery query response
0120: */
0121: public static final String JNP_DISCOVERY_TIMEOUT = "jnp.discoveryTimeout";
0122: /**
0123: * An internal property added by parseNameForScheme if the input name uses a
0124: * url prefix that was removed during cannonicalization. This is needed to
0125: * avoid modification of the incoming Name.
0126: */
0127: public static final String JNP_PARSED_NAME = "jnp.parsedName";
0128: /**
0129: * A flag indicating the style of names passed to NamingManager method.
0130: * True for api expected relative names, false for absolute names as used
0131: * historically by the jboss naming implementation.
0132: */
0133: public static final String JNP_USE_RELATIVE_NAME = "jnp.useRelativeName";
0134: /**
0135: * An integer that controls the number of connection retry attempts will
0136: * be made on the initial connection to the naming server. This only applies
0137: * to ConnectException failures. A value <= 1 means that only one attempt
0138: * will be made.
0139: */
0140: public static final String JNP_MAX_RETRIES = "jnp.maxRetries";
0141:
0142: /**
0143: * The default discovery multicast information
0144: */
0145: public final static String DEFAULT_DISCOVERY_GROUP_ADDRESS = "230.0.0.4";
0146: public final static int DEFAULT_DISCOVERY_GROUP_PORT = 1102;
0147: public final static int DEFAULT_DISCOVERY_TIMEOUT = 5000;
0148:
0149: /**
0150: * An obsolete constant replaced by the JNP_MAX_RETRIES value
0151: */
0152: public static int MAX_RETRIES = 1;
0153: /**
0154: * The JBoss logging interface
0155: */
0156: private static Logger log = Logger.getLogger(NamingContext.class);
0157:
0158: // Static --------------------------------------------------------
0159:
0160: public static Hashtable haServers = new Hashtable();
0161:
0162: public static void setHANamingServerForPartition(
0163: String partitionName, Naming haServer) {
0164: haServers.put(partitionName, haServer);
0165: }
0166:
0167: public static void removeHANamingServerForPartition(
0168: String partitionName) {
0169: haServers.remove(partitionName);
0170: }
0171:
0172: public static Naming getHANamingServerForPartition(
0173: String partitionName) {
0174: return (Naming) haServers.get(partitionName);
0175: }
0176:
0177: public static Naming localServer;
0178:
0179: // Attributes ----------------------------------------------------
0180: Naming naming;
0181: Hashtable env;
0182: Name prefix;
0183:
0184: NameParser parser = new NamingParser();
0185:
0186: // Static --------------------------------------------------------
0187:
0188: // Cache of naming server stubs
0189: // This is a critical optimization in the case where new InitialContext
0190: // is performed often. The server stub will be shared between all those
0191: // calls, which will improve performance.
0192: // Weak references are used so if no contexts use a particular server
0193: // it will be removed from the cache.
0194: static HashMap cachedServers = new HashMap();
0195:
0196: static void addServer(String name, Naming server) {
0197: // Add server to map
0198: synchronized (NamingContext.class) {
0199: cachedServers.put(name, new WeakReference(server));
0200: }
0201: }
0202:
0203: static Naming getServer(String host, int port, Hashtable serverEnv)
0204: throws NamingException {
0205: // Check the server cache for a host:port entry
0206: String hostKey = host + ":" + port;
0207: WeakReference ref = (WeakReference) cachedServers.get(hostKey);
0208: Naming server;
0209: if (ref != null) {
0210: server = (Naming) ref.get();
0211: if (server != null) {
0212: return server;
0213: }
0214: }
0215:
0216: // Server not found; add it to cache
0217: try {
0218: SocketFactory factory = loadSocketFactory(serverEnv);
0219: Socket s;
0220:
0221: try {
0222: InetAddress localAddr = null;
0223: int localPort = 0;
0224: String localAddrStr = (String) serverEnv
0225: .get(JNP_LOCAL_ADDRESS);
0226: String localPortStr = (String) serverEnv
0227: .get(JNP_LOCAL_PORT);
0228: if (localAddrStr != null)
0229: localAddr = InetAddress.getByName(localAddrStr);
0230: if (localPortStr != null)
0231: localPort = Integer.parseInt(localPortStr);
0232: s = factory.createSocket(host, port, localAddr,
0233: localPort);
0234: } catch (IOException e) {
0235: NamingException ex = new ServiceUnavailableException(
0236: "Failed to connect to server " + hostKey);
0237: ex.setRootCause(e);
0238: throw ex;
0239: }
0240:
0241: // Get stub from naming server
0242: BufferedInputStream bis = new BufferedInputStream(s
0243: .getInputStream());
0244: ObjectInputStream in = new ObjectInputStream(bis);
0245: MarshalledObject stub = (MarshalledObject) in.readObject();
0246: server = (Naming) stub.get();
0247: s.close();
0248:
0249: // Add it to cache
0250: addServer(hostKey, server);
0251: serverEnv.put("hostKey", hostKey);
0252:
0253: return server;
0254: } catch (IOException e) {
0255: NamingException ex = new CommunicationException(
0256: "Failed to retrieve stub from server " + hostKey);
0257: ex.setRootCause(e);
0258: throw ex;
0259: } catch (Exception e) {
0260: NamingException ex = new CommunicationException(
0261: "Failed to connect to server " + hostKey);
0262: ex.setRootCause(e);
0263: throw ex;
0264: }
0265: }
0266:
0267: /**
0268: * Create a SocketFactory based on the JNP_SOCKET_FACTORY property in the
0269: * given env. If JNP_SOCKET_FACTORY is not specified default to the
0270: * TimedSocketFactory.
0271: */
0272: static SocketFactory loadSocketFactory(Hashtable serverEnv)
0273: throws ClassNotFoundException, IllegalAccessException,
0274: InstantiationException, InvocationTargetException {
0275: SocketFactory factory = null;
0276:
0277: // Get the socket factory classname
0278: String socketFactoryName = (String) serverEnv
0279: .get(JNP_SOCKET_FACTORY);
0280: if (socketFactoryName == null
0281: || socketFactoryName.equals(TimedSocketFactory.class
0282: .getName())) {
0283: factory = new TimedSocketFactory(serverEnv);
0284: return factory;
0285: }
0286:
0287: /* Create the socket factory. Look for a ctor that accepts a
0288: Hashtable and if not found use the default ctor.
0289: */
0290: ClassLoader loader = Thread.currentThread()
0291: .getContextClassLoader();
0292: Class factoryClass = loader.loadClass(socketFactoryName);
0293: try {
0294: Class[] ctorSig = { Hashtable.class };
0295: Constructor ctor = factoryClass.getConstructor(ctorSig);
0296: Object[] ctorArgs = { serverEnv };
0297: factory = (SocketFactory) ctor.newInstance(ctorArgs);
0298: } catch (NoSuchMethodException e) {
0299: // Use the default ctor
0300: factory = (SocketFactory) factoryClass.newInstance();
0301: }
0302: return factory;
0303: }
0304:
0305: static void removeServer(Hashtable serverEnv) {
0306: String host = "localhost";
0307: int port = 1099;
0308:
0309: // Locate naming service
0310: if (serverEnv.get(Context.PROVIDER_URL) != null) {
0311: String providerURL = (String) serverEnv
0312: .get(Context.PROVIDER_URL);
0313:
0314: StringTokenizer tokenizer = new StringTokenizer(
0315: providerURL, ", ");
0316: while (tokenizer.hasMoreElements()) {
0317: String url = tokenizer.nextToken();
0318:
0319: try {
0320: // Parse the url into a host:port form, stripping any protocol
0321: Name urlAsName = new NamingParser().parse(url);
0322: String server = parseNameForScheme(urlAsName, null);
0323: if (server != null)
0324: url = server;
0325: int colon = url.indexOf(':');
0326: if (colon < 0) {
0327: host = url.trim();
0328: } else {
0329: host = url.substring(0, colon).trim();
0330: try {
0331: port = Integer.parseInt(url.substring(
0332: colon + 1).trim());
0333: } catch (Exception ex) {
0334: // Use default;
0335: }
0336: }
0337:
0338: // Remove server from map
0339: synchronized (NamingContext.class) {
0340: cachedServers.remove(host + ":" + port);
0341: }
0342: } catch (NamingException ignored) {
0343: }
0344: }
0345: Object hostKey = serverEnv.remove("hostKey");
0346: if (hostKey != null) {
0347: synchronized (NamingContext.class) {
0348: cachedServers.remove(hostKey);
0349: }
0350: }
0351: } else {
0352: // Don't do anything for local server
0353: }
0354: }
0355:
0356: /**
0357: * Called to remove any url scheme atoms and extract the naming service
0358: * hostname:port information.
0359: * @param n the name component to the parsed. After returning n will have all
0360: * scheme related atoms removed.
0361: * @return the naming service hostname:port information string if name
0362: * contained the host information.
0363: */
0364: static String parseNameForScheme(Name n, Hashtable nameEnv)
0365: throws InvalidNameException {
0366: String serverInfo = null;
0367: if (n.size() > 0) {
0368: String scheme = n.get(0);
0369: int schemeLength = 0;
0370: if (scheme.startsWith("java:"))
0371: schemeLength = 5;
0372: else if (scheme.startsWith("jnp:"))
0373: schemeLength = 4;
0374: else if (scheme.startsWith("jnps:"))
0375: schemeLength = 5;
0376: else if (scheme.startsWith("jnp-http:"))
0377: schemeLength = 9;
0378: else if (scheme.startsWith("jnp-https:"))
0379: schemeLength = 10;
0380: if (schemeLength > 0) {
0381: // Make a copy of the name to avoid
0382: n = (Name) n.clone();
0383: String suffix = scheme.substring(schemeLength);
0384: if (suffix.length() == 0) {
0385: // Scheme was "url:/..."
0386: n.remove(0);
0387: if (n.size() > 1 && n.get(0).equals("")) {
0388: // Scheme was "url://hostname:port/..."
0389: // Get hostname:port value for the naming server
0390: serverInfo = n.get(1);
0391: n.remove(0);
0392: n.remove(0);
0393: // If n is a empty atom remove it or else a '/' will result
0394: if (n.size() == 1 && n.get(0).length() == 0)
0395: n.remove(0);
0396: }
0397: } else {
0398: // Scheme was "url:foo" -> reinsert "foo"
0399: n.remove(0);
0400: n.add(0, suffix);
0401: }
0402: if (nameEnv != null)
0403: nameEnv.put(JNP_PARSED_NAME, n);
0404: }
0405: }
0406: return serverInfo;
0407: }
0408:
0409: public static void setLocal(Naming server) {
0410: localServer = server;
0411: }
0412:
0413: // Constructors --------------------------------------------------
0414: public NamingContext(Hashtable e, Name baseName, Naming server)
0415: throws NamingException {
0416: if (baseName == null)
0417: this .prefix = parser.parse("");
0418: else
0419: this .prefix = baseName;
0420:
0421: if (e != null)
0422: this .env = (Hashtable) e.clone();
0423: else
0424: this .env = new Hashtable();
0425:
0426: this .naming = server;
0427: }
0428:
0429: // Public --------------------------------------------------------
0430: public Naming getNaming() {
0431: return this .naming;
0432: }
0433:
0434: public void setNaming(Naming server) {
0435: this .naming = server;
0436: }
0437:
0438: // Context implementation ----------------------------------------
0439: public void rebind(String name, Object obj) throws NamingException {
0440: rebind(getNameParser(name).parse(name), obj);
0441: }
0442:
0443: public void rebind(Name name, Object obj) throws NamingException {
0444: Hashtable refEnv = getEnv(name);
0445: checkRef(refEnv);
0446: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0447: if (parsedName != null)
0448: name = parsedName;
0449:
0450: // Allow state factories to change the stored object
0451: obj = getStateToBind(obj, name, refEnv);
0452:
0453: try {
0454: String className = null;
0455:
0456: // Referenceable
0457: if (obj instanceof Referenceable)
0458: obj = ((Referenceable) obj).getReference();
0459:
0460: if (!(obj instanceof Reference)) {
0461: if (obj != null)
0462: className = obj.getClass().getName();
0463: // Normal object - serialize using a MarshalledValuePair
0464: obj = new MarshalledValuePair(obj);
0465: } else {
0466: className = ((Reference) obj).getClassName();
0467: }
0468: naming.rebind(getAbsoluteName(name), obj, className);
0469: } catch (CannotProceedException cpe) {
0470: cpe.setEnvironment(refEnv);
0471: Context cctx = NamingManager.getContinuationContext(cpe);
0472: cctx.rebind(cpe.getRemainingName(), obj);
0473: } catch (IOException e) {
0474: naming = null;
0475: removeServer(refEnv);
0476: NamingException ex = new CommunicationException();
0477: ex.setRootCause(e);
0478: throw ex;
0479: }
0480: }
0481:
0482: public void bind(String name, Object obj) throws NamingException {
0483: bind(getNameParser(name).parse(name), obj);
0484: }
0485:
0486: public void bind(Name name, Object obj) throws NamingException {
0487: Hashtable refEnv = getEnv(name);
0488: checkRef(refEnv);
0489: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0490: if (parsedName != null)
0491: name = parsedName;
0492:
0493: // Allow state factories to change the stored object
0494: obj = getStateToBind(obj, name, refEnv);
0495:
0496: try {
0497: String className = null;
0498:
0499: // Referenceable
0500: if (obj instanceof Referenceable)
0501: obj = ((Referenceable) obj).getReference();
0502:
0503: if (!(obj instanceof Reference)) {
0504: if (obj != null)
0505: className = obj.getClass().getName();
0506:
0507: // Normal object - serialize using a MarshalledValuePair
0508: obj = new MarshalledValuePair(obj);
0509: } else {
0510: className = ((Reference) obj).getClassName();
0511: }
0512: name = getAbsoluteName(name);
0513: naming.bind(name, obj, className);
0514: } catch (CannotProceedException cpe) {
0515: cpe.setEnvironment(refEnv);
0516: Context cctx = NamingManager.getContinuationContext(cpe);
0517: cctx.bind(cpe.getRemainingName(), obj);
0518: } catch (IOException e) {
0519: naming = null;
0520: removeServer(refEnv);
0521: NamingException ex = new CommunicationException();
0522: ex.setRootCause(e);
0523: throw ex;
0524: }
0525: }
0526:
0527: public Object lookup(String name) throws NamingException {
0528: return lookup(getNameParser(name).parse(name));
0529: }
0530:
0531: public Object lookup(Name name) throws NamingException {
0532: Hashtable refEnv = getEnv(name);
0533: checkRef(refEnv);
0534: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0535: if (parsedName != null)
0536: name = parsedName;
0537:
0538: // Empty?
0539: if (name.isEmpty())
0540: return new NamingContext(refEnv, prefix, naming);
0541:
0542: try {
0543: int maxTries = 1;
0544: try {
0545: String n = (String) refEnv.get(JNP_MAX_RETRIES);
0546: if (n != null)
0547: maxTries = Integer.parseInt(n);
0548: if (maxTries <= 0)
0549: maxTries = 1;
0550: } catch (Exception e) {
0551: log.debug("Failed to get JNP_MAX_RETRIES, using 1", e);
0552: }
0553: Name n = getAbsoluteName(name);
0554: Object res = null;
0555: boolean trace = log.isTraceEnabled();
0556: for (int i = 0; i < maxTries; i++) {
0557: try {
0558: res = naming.lookup(n);
0559: break;
0560: } catch (ConnectException ce) {
0561: int retries = maxTries - i - 1;
0562: if (trace)
0563: log.trace("Connect failed, retry count: "
0564: + retries, ce);
0565: // We may overload server so sleep and retry
0566: if (retries > 0) {
0567: try {
0568: Thread.sleep(1);
0569: } catch (InterruptedException ignored) {
0570: }
0571: continue;
0572: }
0573: // Throw the exception to flush the bad server
0574: throw ce;
0575: }
0576: }
0577: if (res instanceof MarshalledValuePair) {
0578: MarshalledValuePair mvp = (MarshalledValuePair) res;
0579: Object storedObj = mvp.get();
0580: return getObjectInstanceWrapFailure(storedObj, name,
0581: refEnv);
0582: } else if (res instanceof MarshalledObject) {
0583: MarshalledObject mo = (MarshalledObject) res;
0584: return mo.get();
0585: } else if (res instanceof Context) {
0586: // Add env
0587: Enumeration keys = refEnv.keys();
0588: while (keys.hasMoreElements()) {
0589: String key = (String) keys.nextElement();
0590: ((Context) res).addToEnvironment(key, refEnv
0591: .get(key));
0592: }
0593: return res;
0594: } else if (res instanceof ResolveResult) {
0595: // Dereference partial result
0596: ResolveResult rr = (ResolveResult) res;
0597: Object resolveRes = rr.getResolvedObj();
0598: Object context;
0599: Object instanceID;
0600:
0601: if (resolveRes instanceof LinkRef) {
0602: context = resolveLink(resolveRes, null);
0603: instanceID = ((LinkRef) resolveRes).getLinkName();
0604: } else {
0605: context = getObjectInstanceWrapFailure(resolveRes,
0606: name, refEnv);
0607: instanceID = context;
0608: }
0609:
0610: if ((context instanceof Context) == false) {
0611: throw new NotContextException(instanceID
0612: + " is not a Context");
0613: }
0614: Context ncontext = (Context) context;
0615: return ncontext.lookup(rr.getRemainingName());
0616: } else if (res instanceof LinkRef) {
0617: // Dereference link
0618: res = resolveLink(res, refEnv);
0619: } else if (res instanceof Reference) {
0620: // Dereference object
0621: res = getObjectInstanceWrapFailure(res, name, refEnv);
0622: if (res instanceof LinkRef)
0623: res = resolveLink(res, refEnv);
0624: }
0625:
0626: return res;
0627: } catch (CannotProceedException cpe) {
0628: cpe.setEnvironment(refEnv);
0629: Context cctx = NamingManager.getContinuationContext(cpe);
0630: return cctx.lookup(cpe.getRemainingName());
0631: } catch (IOException e) {
0632: naming = null;
0633: removeServer(refEnv);
0634: NamingException ex = new CommunicationException();
0635: ex.setRootCause(e);
0636: throw ex;
0637: } catch (ClassNotFoundException e) {
0638: NamingException ex = new CommunicationException();
0639: ex.setRootCause(e);
0640: throw ex;
0641: }
0642: }
0643:
0644: public void unbind(String name) throws NamingException {
0645: unbind(getNameParser(name).parse(name));
0646: }
0647:
0648: public void unbind(Name name) throws NamingException {
0649: Hashtable refEnv = getEnv(name);
0650: checkRef(refEnv);
0651: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0652: if (parsedName != null)
0653: name = parsedName;
0654:
0655: try {
0656: naming.unbind(getAbsoluteName(name));
0657: } catch (CannotProceedException cpe) {
0658: cpe.setEnvironment(refEnv);
0659: Context cctx = NamingManager.getContinuationContext(cpe);
0660: cctx.unbind(cpe.getRemainingName());
0661: } catch (IOException e) {
0662: naming = null;
0663: removeServer(refEnv);
0664: NamingException ex = new CommunicationException();
0665: ex.setRootCause(e);
0666: throw ex;
0667: }
0668: }
0669:
0670: public void rename(String oldname, String newname)
0671: throws NamingException {
0672: rename(getNameParser(oldname).parse(oldname), getNameParser(
0673: newname).parse(newname));
0674: }
0675:
0676: public void rename(Name oldName, Name newName)
0677: throws NamingException {
0678: bind(newName, lookup(oldName));
0679: unbind(oldName);
0680: }
0681:
0682: public NamingEnumeration list(String name) throws NamingException {
0683: return list(getNameParser(name).parse(name));
0684: }
0685:
0686: public NamingEnumeration list(Name name) throws NamingException {
0687: Hashtable refEnv = getEnv(name);
0688: checkRef(refEnv);
0689: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0690: if (parsedName != null)
0691: name = parsedName;
0692:
0693: try {
0694: return new NamingEnumerationImpl(naming
0695: .list(getAbsoluteName(name)));
0696: } catch (CannotProceedException cpe) {
0697: cpe.setEnvironment(refEnv);
0698: Context cctx = NamingManager.getContinuationContext(cpe);
0699: return cctx.list(cpe.getRemainingName());
0700: } catch (IOException e) {
0701: naming = null;
0702: removeServer(refEnv);
0703: NamingException ex = new CommunicationException();
0704: ex.setRootCause(e);
0705: throw ex;
0706: }
0707: }
0708:
0709: public NamingEnumeration listBindings(String name)
0710: throws NamingException {
0711: return listBindings(getNameParser(name).parse(name));
0712: }
0713:
0714: public NamingEnumeration listBindings(Name name)
0715: throws NamingException {
0716: Hashtable refEnv = getEnv(name);
0717: checkRef(refEnv);
0718: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0719: if (parsedName != null)
0720: name = parsedName;
0721:
0722: try {
0723: // Get list
0724: Collection bindings = naming
0725: .listBindings(getAbsoluteName(name));
0726: Collection realBindings = new ArrayList(bindings.size());
0727:
0728: // Convert marshalled objects
0729: Iterator i = bindings.iterator();
0730: while (i.hasNext()) {
0731: Binding binding = (Binding) i.next();
0732: Object obj = binding.getObject();
0733: if (obj instanceof MarshalledValuePair) {
0734: try {
0735: obj = ((MarshalledValuePair) obj).get();
0736: } catch (ClassNotFoundException e) {
0737: NamingException ex = new CommunicationException();
0738: ex.setRootCause(e);
0739: throw ex;
0740: }
0741: } else if (obj instanceof MarshalledObject) {
0742: try {
0743: obj = ((MarshalledObject) obj).get();
0744: } catch (ClassNotFoundException e) {
0745: NamingException ex = new CommunicationException();
0746: ex.setRootCause(e);
0747: throw ex;
0748: }
0749: }
0750: realBindings.add(new Binding(binding.getName(), binding
0751: .getClassName(), obj));
0752: }
0753:
0754: // Return transformed list of bindings
0755: return new NamingEnumerationImpl(realBindings);
0756: } catch (CannotProceedException cpe) {
0757: cpe.setEnvironment(refEnv);
0758: Context cctx = NamingManager.getContinuationContext(cpe);
0759: return cctx.listBindings(cpe.getRemainingName());
0760: } catch (IOException e) {
0761: naming = null;
0762: removeServer(refEnv);
0763: NamingException ex = new CommunicationException();
0764: ex.setRootCause(e);
0765: throw ex;
0766: }
0767: }
0768:
0769: public String composeName(String name, String prefix)
0770: throws NamingException {
0771: Name result = composeName(parser.parse(name), parser
0772: .parse(prefix));
0773: return result.toString();
0774: }
0775:
0776: public Name composeName(Name name, Name prefix)
0777: throws NamingException {
0778: Name result = (Name) (prefix.clone());
0779: result.addAll(name);
0780: return result;
0781: }
0782:
0783: public NameParser getNameParser(String name) throws NamingException {
0784: return parser;
0785: }
0786:
0787: public NameParser getNameParser(Name name) throws NamingException {
0788: return getNameParser(name.toString());
0789: }
0790:
0791: public Context createSubcontext(String name) throws NamingException {
0792: return createSubcontext(getNameParser(name).parse(name));
0793: }
0794:
0795: public Context createSubcontext(Name name) throws NamingException {
0796: if (name.size() == 0)
0797: throw new InvalidNameException(
0798: "Cannot pass an empty name to createSubcontext");
0799:
0800: Hashtable refEnv = getEnv(name);
0801: checkRef(refEnv);
0802: Name parsedName = (Name) refEnv.get(JNP_PARSED_NAME);
0803: if (parsedName != null)
0804: name = parsedName;
0805:
0806: try {
0807: name = getAbsoluteName(name);
0808: return naming.createSubcontext(name);
0809: } catch (CannotProceedException cpe) {
0810: cpe.setEnvironment(refEnv);
0811: Context cctx = NamingManager.getContinuationContext(cpe);
0812: return cctx.createSubcontext(cpe.getRemainingName());
0813: } catch (IOException e) {
0814: naming = null;
0815: removeServer(refEnv);
0816: NamingException ex = new CommunicationException();
0817: ex.setRootCause(e);
0818: throw ex;
0819: }
0820: }
0821:
0822: public Object addToEnvironment(String propName, Object propVal)
0823: throws NamingException {
0824: Object old = env.get(propName);
0825: env.put(propName, propVal);
0826: return old;
0827: }
0828:
0829: public Object removeFromEnvironment(String propName)
0830: throws NamingException {
0831: return env.remove(propName);
0832: }
0833:
0834: public Hashtable getEnvironment() throws NamingException {
0835: return env;
0836: }
0837:
0838: public void close() throws NamingException {
0839: env = null;
0840: naming = null;
0841: }
0842:
0843: public String getNameInNamespace() throws NamingException {
0844: return prefix.toString();
0845: }
0846:
0847: public void destroySubcontext(String name) throws NamingException {
0848: throw new OperationNotSupportedException();
0849: }
0850:
0851: public void destroySubcontext(Name name) throws NamingException {
0852: throw new OperationNotSupportedException();
0853: }
0854:
0855: public Object lookupLink(String name) throws NamingException {
0856: return lookupLink(getNameParser(name).parse(name));
0857: }
0858:
0859: /**
0860: * Lookup the object referred to by name but don't dereferrence the final
0861: * component. This really just involves returning the raw value returned by
0862: * the Naming.lookup() method.
0863: * @return the raw object bound under name.
0864: */
0865: public Object lookupLink(Name name) throws NamingException {
0866: if (name.isEmpty())
0867: return lookup(name);
0868:
0869: Object link = null;
0870: try {
0871: Name n = getAbsoluteName(name);
0872: link = naming.lookup(n);
0873: if (!(link instanceof LinkRef) && link instanceof Reference)
0874: link = getObjectInstance(link, name, null);
0875: ;
0876: } catch (IOException e) {
0877: naming = null;
0878: removeServer(env);
0879: NamingException ex = new CommunicationException();
0880: ex.setRootCause(e);
0881: throw ex;
0882: } catch (Exception e) {
0883: NamingException ex = new NamingException(
0884: "Could not lookup link");
0885: ex.setRemainingName(name);
0886: ex.setRootCause(e);
0887: throw ex;
0888: }
0889: return link;
0890: }
0891:
0892: protected Object resolveLink(Object res, Hashtable refEnv)
0893: throws NamingException {
0894: Object linkResult = null;
0895: try {
0896: LinkRef link = (LinkRef) res;
0897: String ref = link.getLinkName();
0898: if (ref.startsWith("./"))
0899: linkResult = lookup(ref.substring(2));
0900: else if (refEnv != null)
0901: linkResult = new InitialContext(refEnv).lookup(ref);
0902: else
0903: linkResult = new InitialContext().lookup(ref);
0904: } catch (Exception e) {
0905: NamingException ex = new NamingException(
0906: "Could not dereference object");
0907: ex.setRootCause(e);
0908: throw ex;
0909: }
0910: return linkResult;
0911: }
0912:
0913: // Private -------------------------------------------------------
0914:
0915: /**
0916: * Determine the form of the name to pass to the NamingManager operations.
0917: * This is supposed to be a context relative name according to the javaodcs
0918: * for NamingManager, but historically the absolute name of the target
0919: * context has been passed in.
0920: *
0921: * @param env - the env of NamingContext that op was called on
0922: * @return true if the legacy and technically incorrect absolute name should
0923: * be used, false if the context relative name should be used.
0924: */
0925: private boolean useAbsoluteName(Hashtable env) {
0926: if (env == null)
0927: return true;
0928: String useRelativeName = (String) env
0929: .get(JNP_USE_RELATIVE_NAME);
0930: return Boolean.valueOf(useRelativeName) == Boolean.FALSE;
0931: }
0932:
0933: /**
0934: * Use the NamingManager.getStateToBind to obtain the actual object to bind
0935: * into jndi.
0936: * @param obj - the value passed to bind/rebind
0937: * @param name - the name passed to bind/rebind
0938: * @param env - the env of NamingContext that bind/rebind was called on
0939: * @return the object to bind to the naming server
0940: * @throws NamingException
0941: */
0942: private Object getStateToBind(Object obj, Name name, Hashtable env)
0943: throws NamingException {
0944: if (useAbsoluteName(env))
0945: name = getAbsoluteName(name);
0946: return NamingManager.getStateToBind(obj, name, this , env);
0947: }
0948:
0949: /**
0950: * Use the NamingManager.getObjectInstance to resolve the raw object obtained
0951: * from the naming server.
0952: * @param obj - raw value obtained from the naming server
0953: * @param name - the name passed to the lookup op
0954: * @param env - the env of NamingContext that the op was called on
0955: * @return the fully resolved object
0956: * @throws Exception
0957: */
0958: private Object getObjectInstance(Object obj, Name name,
0959: Hashtable env) throws Exception {
0960: if (useAbsoluteName(env))
0961: name = getAbsoluteName(name);
0962: return NamingManager.getObjectInstance(obj, name, this , env);
0963: }
0964:
0965: /**
0966: * Resolve the final object and wrap any non-NamingException errors in a
0967: * NamingException with the cause passed as the root cause.
0968: * @param obj - raw value obtained from the naming server
0969: * @param name - the name passed to the lookup op
0970: * @param env - the env of NamingContext that the op was called on
0971: * @return the fully resolved object
0972: * @throws NamingException
0973: */
0974: private Object getObjectInstanceWrapFailure(Object obj, Name name,
0975: Hashtable env) throws NamingException {
0976: try {
0977: return getObjectInstance(obj, name, env);
0978: } catch (NamingException e) {
0979: throw e;
0980: } catch (Exception e) {
0981: NamingException ex = new NamingException(
0982: "Could not dereference object");
0983: ex.setRootCause(e);
0984: throw ex;
0985: }
0986: }
0987:
0988: /**
0989: * This methods sends a broadcast message on the network and asks and HA-JNDI
0990: * server to sent it the HA-JNDI stub
0991: */
0992: private Naming discoverServer(Hashtable serverEnv)
0993: throws NamingException {
0994: boolean trace = log.isTraceEnabled();
0995: // Check if discovery should be done
0996: String disableDiscovery = (String) serverEnv
0997: .get(JNP_DISABLE_DISCOVERY);
0998: if (Boolean.valueOf(disableDiscovery) == Boolean.TRUE) {
0999: if (trace)
1000: log.trace("Skipping discovery due to disable flag");
1001: return null;
1002: }
1003:
1004: // we first try to discover the server locally
1005: //
1006: String partitionName = (String) serverEnv
1007: .get(JNP_PARTITION_NAME);
1008: Naming server = null;
1009: if (partitionName != null) {
1010: server = getHANamingServerForPartition(partitionName);
1011: if (server != null)
1012: return server;
1013: }
1014:
1015: // We next broadcast a HelloWorld datagram (multicast)
1016: // Any listening server will answer with its IP address:port in another datagram
1017: // we will then use this to make a standard "lookup"
1018: //
1019: MulticastSocket s = null;
1020: InetAddress iaGroup = null;
1021: try {
1022: String group = DEFAULT_DISCOVERY_GROUP_ADDRESS;
1023: int port = DEFAULT_DISCOVERY_GROUP_PORT;
1024: int timeout = DEFAULT_DISCOVERY_TIMEOUT;
1025:
1026: int ttl = 16;
1027:
1028: String discoveryTTL = (String) serverEnv
1029: .get(JNP_DISCOVERY_TTL);
1030: if (discoveryTTL != null)
1031: ttl = Integer.parseInt(discoveryTTL);
1032:
1033: String discoveryGroup = (String) serverEnv
1034: .get(JNP_DISCOVERY_GROUP);
1035: if (discoveryGroup != null)
1036: group = discoveryGroup;
1037: String discoveryTimeout = (String) serverEnv
1038: .get(JNP_DISCOVERY_TIMEOUT);
1039: if (discoveryTimeout == null) {
1040: // Check the old property name
1041: discoveryTimeout = (String) serverEnv
1042: .get("DISCOVERY_TIMEOUT");
1043: }
1044: if (discoveryTimeout != null
1045: && !discoveryTimeout.equals(""))
1046: timeout = Integer.parseInt(discoveryTimeout);
1047:
1048: String discoveryGroupPort = (String) serverEnv
1049: .get(JNP_DISCOVERY_PORT);
1050: if (discoveryGroupPort == null) {
1051: // Check the old property name
1052: discoveryGroupPort = (String) serverEnv
1053: .get("DISCOVERY_GROUP");
1054: }
1055: if (discoveryGroupPort != null
1056: && !discoveryGroupPort.equals("")) {
1057: int colon = discoveryGroupPort.indexOf(':');
1058: if (colon < 0) {
1059: // No group given, just the port
1060: try {
1061: port = Integer.parseInt(discoveryGroupPort);
1062: } catch (Exception ex) {
1063: log.warn("Failed to parse port: "
1064: + discoveryGroupPort, ex);
1065: }
1066: } else {
1067: // The old group:port syntax was given
1068: group = discoveryGroupPort.substring(0, colon);
1069: String portStr = discoveryGroupPort
1070: .substring(colon + 1);
1071: try {
1072: port = Integer.parseInt(portStr);
1073: } catch (Exception ex) {
1074: log
1075: .warn("Failed to parse port: "
1076: + portStr, ex);
1077: }
1078: }
1079: }
1080:
1081: iaGroup = InetAddress.getByName(group);
1082: String localAddrStr = (String) serverEnv
1083: .get(JNP_LOCAL_ADDRESS);
1084: String localPortStr = (String) serverEnv
1085: .get(JNP_LOCAL_PORT);
1086: int localPort = 0;
1087: if (localPortStr != null)
1088: localPort = Integer.parseInt(localPortStr);
1089: if (localAddrStr != null) {
1090: InetSocketAddress localAddr = new InetSocketAddress(
1091: localAddrStr, localPort);
1092: s = new MulticastSocket(localAddr);
1093: } else {
1094: s = new MulticastSocket(localPort);
1095: }
1096: s.setSoTimeout(timeout);
1097: s.setTimeToLive(ttl);
1098: if (log.isTraceEnabled())
1099: log
1100: .trace("TTL on multicast discovery socket is "
1101: + ttl);
1102: s.joinGroup(iaGroup);
1103: if (trace)
1104: log.trace("MulticastSocket: " + s);
1105: DatagramPacket packet;
1106: // Send a request optionally restricted to a cluster partition
1107: StringBuffer data = new StringBuffer("GET_ADDRESS");
1108: if (partitionName != null)
1109: data.append(":" + partitionName);
1110: byte[] buf = data.toString().getBytes();
1111: packet = new DatagramPacket(buf, buf.length, iaGroup, port);
1112: if (trace)
1113: log.trace("Sending discovery packet(" + data + ") to: "
1114: + iaGroup + ":" + port);
1115: s.send(packet);
1116: // Look for a reply
1117: // IP address + port number = 128.128.128.128:65535 => (12+3) + 1 + (5) = 21
1118:
1119: buf = new byte[50];
1120: packet = new DatagramPacket(buf, buf.length);
1121: s.receive(packet);
1122: String myServer = new String(packet.getData()).trim();
1123: if (trace)
1124: log.trace("Received answer packet: " + myServer);
1125: while (myServer != null
1126: && myServer.startsWith("GET_ADDRESS")) {
1127: Arrays.fill(buf, (byte) 0);
1128: packet.setLength(buf.length);
1129: s.receive(packet);
1130: byte[] reply = packet.getData();
1131: myServer = new String(reply).trim();
1132: if (trace)
1133: log.trace("Received answer packet: " + myServer);
1134: }
1135: String serverHost;
1136: int serverPort;
1137:
1138: int colon = myServer.indexOf(':');
1139: if (colon >= 0) {
1140: serverHost = myServer.substring(0, colon);
1141: serverPort = Integer.valueOf(
1142: myServer.substring(colon + 1)).intValue();
1143: server = getServer(serverHost, serverPort, serverEnv);
1144: }
1145: return server;
1146: } catch (IOException e) {
1147: if (trace)
1148: log.trace("Discovery failed", e);
1149: NamingException ex = new CommunicationException(e
1150: .getMessage());
1151: ex.setRootCause(e);
1152: throw ex;
1153: } finally {
1154: try {
1155: if (s != null)
1156: s.leaveGroup(iaGroup);
1157: } catch (Exception ignore) {
1158: }
1159: try {
1160: if (s != null)
1161: s.close();
1162: } catch (Exception ignore) {
1163: }
1164: }
1165: }
1166:
1167: private void checkRef(Hashtable refEnv) throws NamingException {
1168: if (naming == null) {
1169: String host = "localhost";
1170: int port = 1099;
1171: Exception serverEx = null;
1172:
1173: // Locate first available naming service
1174: String urls = (String) refEnv.get(Context.PROVIDER_URL);
1175: if (urls != null && urls.length() > 0) {
1176: StringTokenizer tokenizer = new StringTokenizer(urls,
1177: ",");
1178:
1179: while (naming == null && tokenizer.hasMoreElements()) {
1180: String url = tokenizer.nextToken();
1181: // Parse the url into a host:port form, stripping any protocol
1182: Name urlAsName = getNameParser("").parse(url);
1183: String server = parseNameForScheme(urlAsName, null);
1184: if (server != null)
1185: url = server;
1186: int colon = url.indexOf(':');
1187: if (colon < 0) {
1188: host = url;
1189: } else {
1190: host = url.substring(0, colon).trim();
1191: try {
1192: port = Integer.parseInt(url.substring(
1193: colon + 1).trim());
1194: } catch (Exception ex) {
1195: // Use default;
1196: }
1197: }
1198: try {
1199: // Get server from cache
1200: naming = getServer(host, port, refEnv);
1201: } catch (Exception e) {
1202: serverEx = e;
1203: log.debug("Failed to connect to " + host + ":"
1204: + port, e);
1205: }
1206: }
1207:
1208: // If there is still no
1209: Exception discoveryFailure = null;
1210: if (naming == null) {
1211: try {
1212: naming = discoverServer(refEnv);
1213: } catch (Exception e) {
1214: discoveryFailure = e;
1215: if (serverEx == null)
1216: serverEx = e;
1217: }
1218: if (naming == null) {
1219: StringBuffer buffer = new StringBuffer(50);
1220: buffer
1221: .append(
1222: "Could not obtain connection to any of these urls: ")
1223: .append(urls);
1224: if (discoveryFailure != null)
1225: buffer
1226: .append(
1227: " and discovery failed with error: ")
1228: .append(discoveryFailure);
1229: CommunicationException ce = new CommunicationException(
1230: buffer.toString());
1231: ce.setRootCause(serverEx);
1232: throw ce;
1233: }
1234: }
1235: } else {
1236: // If we are in a clustering scenario, the client code may request a context
1237: // for a *specific* HA-JNDI service (i.e. linked to a *specific* partition)
1238: // EVEN if the lookup is done inside a JBoss VM. For example, a JBoss service
1239: // may do a lookup on a HA-JNDI service running on another host *without*
1240: // explicitly providing a PROVIDER_URL but simply by providing a JNP_PARTITON_NAME
1241: // parameter so that dynamic discovery can be used
1242: //
1243: String jnpPartitionName = (String) refEnv
1244: .get(JNP_PARTITION_NAME);
1245: if (jnpPartitionName != null) {
1246: // the client is requesting for a specific partition name
1247: //
1248: naming = discoverServer(refEnv);
1249: if (naming == null)
1250: throw new ConfigurationException(
1251: "No valid context could be build for jnp.partitionName="
1252: + jnpPartitionName);
1253: } else {
1254: // Use server in same JVM
1255: naming = localServer;
1256:
1257: if (naming == null) {
1258: naming = discoverServer(refEnv);
1259: if (naming == null)
1260: // Local, but no local JNDI provider found!
1261: throw new ConfigurationException(
1262: "No valid Context.PROVIDER_URL was found");
1263: }
1264: }
1265: }
1266: }
1267: }
1268:
1269: private Name getAbsoluteName(Name n) throws NamingException {
1270: if (n.isEmpty())
1271: return composeName(n, prefix);
1272: else if (n.get(0).toString().equals("")) // Absolute name
1273: return n.getSuffix(1);
1274: else
1275: // Add prefix
1276: return composeName(n, prefix);
1277: }
1278:
1279: private Hashtable getEnv(Name n) throws InvalidNameException {
1280: Hashtable nameEnv = env;
1281: env.remove(JNP_PARSED_NAME);
1282: String serverInfo = parseNameForScheme(n, nameEnv);
1283: if (serverInfo != null) {
1284: // Set hostname:port value for the naming server
1285: nameEnv = (Hashtable) env.clone();
1286: nameEnv.put(Context.PROVIDER_URL, serverInfo);
1287: }
1288: return nameEnv;
1289: }
1290:
1291: // Inner classes -------------------------------------------------
1292: }
|