001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j.remote;
010:
011: import java.io.IOException;
012: import java.util.Map;
013: import java.util.StringTokenizer;
014:
015: import javax.management.remote.JMXConnectorServerFactory;
016: import javax.management.remote.JMXServiceURL;
017:
018: import mx4j.log.Logger;
019:
020: /**
021: * ConnectionResolver handles the details of creating connections for different protocols.
022: * Subclasses for the specific protocol are instantiated using a mechanism very similar to the
023: * one specified by {@link javax.management.remote.JMXConnectorFactory}. Here a subclass
024: * has a fully qualified name specified like this:
025: * <package>.resolver.<protocol>.Resolver, for example
026: * {@link mx4j.remote.resolver.rmi.Resolver}
027: * This class is used from both the client and the server.
028: * The former uses it to lookup stubs or connections to the server side; the latter uses it
029: * to create server instances and make them availale to clients, for example via JNDI.
030: * The client and server methods have not been splitted into 2 different interfaces because
031: * most of the times they share common code, although it may have been a better design.
032: *
033: * @version $Revision: 1.6 $
034: */
035: public abstract class ConnectionResolver extends ProviderHelper {
036: /**
037: * Returns a subclass of ConnectionResolver for the specified protocol.
038: */
039: public static ConnectionResolver newConnectionResolver(
040: String proto, Map environment) {
041: String protocol = normalizeProtocol(proto);
042: String resolverPackages = findResolverPackageList();
043: ClassLoader classLoader = findResolverClassLoader(
044: environment,
045: JMXConnectorServerFactory.PROTOCOL_PROVIDER_CLASS_LOADER);
046: return loadResolver(resolverPackages, protocol, classLoader);
047: }
048:
049: private static String findResolverPackageList() {
050: String packages = findSystemPackageList(MX4JRemoteConstants.PROTOCOL_RESOLVER_PACKAGES);
051: if (packages == null)
052: packages = MX4JRemoteConstants.RESOLVER_PACKAGES;
053: else
054: packages += MX4JRemoteConstants.RESOLVER_PACKAGES_SEPARATOR
055: + MX4JRemoteConstants.RESOLVER_PACKAGES;
056: Logger logger = getLogger();
057: if (logger.isEnabledFor(Logger.DEBUG))
058: logger.debug("Resolver packages list is: " + packages);
059: return packages;
060: }
061:
062: private static ClassLoader findResolverClassLoader(Map environment,
063: String loaderKey) {
064: if (environment == null)
065: return Thread.currentThread().getContextClassLoader();
066: Object object = environment.get(loaderKey);
067: if (object == null)
068: return Thread.currentThread().getContextClassLoader();
069: if (!(object instanceof ClassLoader))
070: throw new IllegalArgumentException("Environment property "
071: + loaderKey + " must be a ClassLoader");
072: return (ClassLoader) object;
073: }
074:
075: private static ConnectionResolver loadResolver(String packages,
076: String protocol, ClassLoader loader) {
077: Logger logger = getLogger();
078:
079: StringTokenizer tokenizer = new StringTokenizer(packages,
080: MX4JRemoteConstants.RESOLVER_PACKAGES_SEPARATOR);
081: while (tokenizer.hasMoreTokens()) {
082: String pkg = tokenizer.nextToken().trim();
083: if (logger.isEnabledFor(Logger.DEBUG))
084: logger.debug("Resolver package: " + pkg);
085: if (pkg.length() == 0)
086: continue;
087:
088: String resolverClassName = constructClassName(pkg,
089: protocol, MX4JRemoteConstants.RESOLVER_CLASS);
090:
091: Class resolverClass = null;
092: try {
093: resolverClass = loadClass(resolverClassName, loader);
094: } catch (ClassNotFoundException x) {
095: if (logger.isEnabledFor(Logger.DEBUG))
096: logger
097: .debug("Resolver class "
098: + resolverClassName
099: + " not found, continuing with next package");
100: continue;
101: } catch (Exception x) {
102: if (logger.isEnabledFor(Logger.TRACE))
103: logger.trace("Cannot load resolver class "
104: + resolverClassName, x);
105: return null;
106: }
107:
108: try {
109: return (ConnectionResolver) resolverClass.newInstance();
110: } catch (Exception x) {
111: if (logger.isEnabledFor(Logger.TRACE))
112: logger.trace("Cannot instantiate resolver class "
113: + resolverClassName, x);
114: return null;
115: }
116: }
117:
118: // Nothing found
119: if (logger.isEnabledFor(Logger.DEBUG))
120: logger.debug("Could not find resolver for protocol "
121: + protocol + " in package list '" + packages + "'");
122: return null;
123: }
124:
125: /**
126: * Looks up a connection with the server side as specified in the given JMXServiceURL.
127: * This method is used in implementations of {@link javax.management.remote.JMXConnector#connect()}.
128: *
129: * @see #bindClient
130: */
131: public abstract Object lookupClient(JMXServiceURL url,
132: Map environment) throws IOException;
133:
134: /**
135: * Connects the client returned by {@link #lookupClient} to the server side.
136: *
137: * @return An object of the same type as the client passed in; normally the client object itself
138: */
139: public abstract Object bindClient(Object client, Map environment)
140: throws IOException;
141:
142: /**
143: * Creates an instance of the server as specified in the given JMXServiceURL.
144: * It is only a factory method, it should just return a fresh instance of the server;
145: * other methods are responsible to make it available to clients (for example exporting it).
146: * This method is used in implementations of {@link javax.management.remote.JMXConnectorServer#start}.
147: *
148: * @see #bindServer
149: * @see #destroyServer
150: */
151: public abstract Object createServer(JMXServiceURL url,
152: Map environment) throws IOException;
153:
154: /**
155: * Binds the server created by {@link #createServer} to a place specified by the JMXServiceURL.
156: *
157: * @return a new JMXServiceURL that specifies where the server has been bound to.
158: * @see #unbindServer
159: */
160: public abstract JMXServiceURL bindServer(Object server,
161: JMXServiceURL url, Map environment) throws IOException;
162:
163: /**
164: * Unbinds the server bound by {@link #bindServer} from the place specified by the JMXServiceURL.
165: *
166: * @see #destroyServer
167: */
168: public abstract void unbindServer(Object server,
169: JMXServiceURL address, Map environment) throws IOException;
170:
171: /**
172: * Destroys the server created by {@link #createServer}, by cleaning up resources it may have requested
173: * at creation time
174: *
175: * @see #createServer
176: */
177: public abstract void destroyServer(Object server,
178: JMXServiceURL url, Map environment) throws IOException;
179: }
|