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.resolver.rmi;
010:
011: import java.io.ByteArrayInputStream;
012: import java.io.ByteArrayOutputStream;
013: import java.io.IOException;
014: import java.io.ObjectInputStream;
015: import java.io.ObjectOutputStream;
016: import java.net.MalformedURLException;
017: import java.rmi.Remote;
018: import java.rmi.server.RMIClientSocketFactory;
019: import java.rmi.server.RMIServerSocketFactory;
020: import java.util.Hashtable;
021: import java.util.Map;
022:
023: import javax.management.remote.JMXServiceURL;
024: import javax.management.remote.rmi.RMIConnectorServer;
025: import javax.management.remote.rmi.RMIJRMPServerImpl;
026: import javax.management.remote.rmi.RMIServer;
027: import javax.management.remote.rmi.RMIServerImpl;
028: import javax.naming.InitialContext;
029: import javax.naming.NamingException;
030:
031: import mx4j.log.Logger;
032: import mx4j.remote.ConnectionResolver;
033: import mx4j.util.Base64Codec;
034:
035: /**
036: * Resolver for RMI/JRMP protocol.
037: *
038: * @version $Revision: 1.1 $
039: */
040: public class Resolver extends ConnectionResolver {
041: private static final String JNDI_CONTEXT = "/jndi/";
042: private static final String STUB_CONTEXT = "/stub/";
043:
044: //********************************************************************************************************************//
045: // CLIENT METHODS
046:
047: public Object lookupClient(JMXServiceURL url, Map environment)
048: throws IOException {
049: return lookupRMIServerStub(url, environment);
050: }
051:
052: public Object bindClient(Object client, Map environment)
053: throws IOException {
054: // JRMP does not need anything special
055: return client;
056: }
057:
058: protected RMIServer lookupRMIServerStub(JMXServiceURL url,
059: Map environment) throws IOException {
060: Logger logger = getLogger();
061:
062: String path = url.getURLPath();
063: if (logger.isEnabledFor(Logger.DEBUG))
064: logger.debug("JMXServiceURL for lookup is: '" + url + "'");
065:
066: if (path != null) {
067: if (path.startsWith(JNDI_CONTEXT)) {
068: return lookupStubInJNDI(url, environment);
069: }
070:
071: return decodeStub(url, environment);
072: }
073:
074: throw new MalformedURLException("Unsupported lookup " + url);
075: }
076:
077: private RMIServer lookupStubInJNDI(JMXServiceURL url,
078: Map environment) throws IOException {
079: Logger logger = getLogger();
080:
081: String path = url.getURLPath();
082: String name = path.substring(JNDI_CONTEXT.length());
083: if (logger.isEnabledFor(Logger.DEBUG))
084: logger.debug("Looking up RMI stub in JNDI under name "
085: + name);
086:
087: InitialContext ctx = null;
088: try {
089: ctx = new InitialContext(new Hashtable(environment));
090: Object stub = ctx.lookup(name);
091: if (logger.isEnabledFor(Logger.DEBUG))
092: logger.debug("Found RMI stub in JNDI " + stub);
093: return narrowRMIServerStub(stub);
094: } catch (NamingException x) {
095: if (logger.isEnabledFor(Logger.DEBUG))
096: logger.debug("Cannot lookup RMI stub in JNDI", x);
097: throw new IOException(x.toString());
098: } finally {
099: try {
100: if (ctx != null)
101: ctx.close();
102: } catch (NamingException x) {
103: if (logger.isEnabledFor(Logger.DEBUG))
104: logger.debug("Cannot close InitialContext", x);
105: }
106: }
107: }
108:
109: protected RMIServer narrowRMIServerStub(Object stub) {
110: return (RMIServer) stub;
111: }
112:
113: protected RMIServer decodeStub(JMXServiceURL url, Map environment)
114: throws IOException {
115: String path = url.getURLPath();
116: if (path.startsWith(STUB_CONTEXT)) {
117: byte[] encoded = path.substring(STUB_CONTEXT.length())
118: .getBytes();
119: if (!Base64Codec.isArrayByteBase64(encoded))
120: throw new IOException(
121: "Encoded stub form is not a valid Base64 sequence: "
122: + url);
123: byte[] decoded = Base64Codec.decodeBase64(encoded);
124: ByteArrayInputStream bais = new ByteArrayInputStream(
125: decoded);
126: ObjectInputStream ois = null;
127: try {
128: ois = new ObjectInputStream(bais);
129: return (RMIServer) ois.readObject();
130: } catch (ClassNotFoundException x) {
131: throw new IOException("Cannot decode stub from " + url
132: + ": " + x);
133: } finally {
134: if (ois != null)
135: ois.close();
136: }
137: }
138: throw new MalformedURLException("Unsupported binding: " + url);
139: }
140:
141: //********************************************************************************************************************//
142: // SERVER METHODS
143:
144: public Object createServer(JMXServiceURL url, Map environment)
145: throws IOException {
146: return createRMIServer(url, environment);
147: }
148:
149: protected RMIServerImpl createRMIServer(JMXServiceURL url,
150: Map environment) throws IOException {
151: int port = url.getPort();
152: RMIClientSocketFactory clientFactory = (RMIClientSocketFactory) environment
153: .get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE);
154: RMIServerSocketFactory serverFactory = (RMIServerSocketFactory) environment
155: .get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE);
156: return new RMIJRMPServerImpl(port, clientFactory,
157: serverFactory, environment);
158: }
159:
160: public JMXServiceURL bindServer(Object server, JMXServiceURL url,
161: Map environment) throws IOException {
162: // See javax/management/remote/rmi/package-summary.html
163:
164: RMIServerImpl rmiServer = (RMIServerImpl) server;
165:
166: Logger logger = getLogger();
167: if (logger.isEnabledFor(Logger.DEBUG))
168: logger.debug("JMXServiceURL for binding is: '" + url + "'");
169:
170: if (isEncodedForm(url)) {
171: String path = encodeStub(rmiServer, environment);
172: return new JMXServiceURL(url.getProtocol(), url.getHost(),
173: url.getPort(), path);
174: } else {
175: String jndiURL = parseJNDIForm(url);
176: if (logger.isEnabledFor(Logger.DEBUG))
177: logger.debug("JMXServiceURL path for binding is: '"
178: + jndiURL + "'");
179:
180: InitialContext ctx = null;
181: try {
182: ctx = new InitialContext(new Hashtable(environment));
183: boolean rebind = Boolean
184: .valueOf(
185: (String) environment
186: .get(RMIConnectorServer.JNDI_REBIND_ATTRIBUTE))
187: .booleanValue();
188: if (rebind)
189: ctx.rebind(jndiURL, rmiServer.toStub());
190: else
191: ctx.bind(jndiURL, rmiServer.toStub());
192: if (logger.isEnabledFor(Logger.DEBUG))
193: logger.debug("Bound " + rmiServer + " to "
194: + jndiURL);
195: return url;
196: } catch (NamingException x) {
197: if (logger.isEnabledFor(Logger.DEBUG))
198: logger.debug("Cannot bind server " + rmiServer
199: + " to " + jndiURL, x);
200: throw new IOException(x.toString());
201: } finally {
202: try {
203: if (ctx != null)
204: ctx.close();
205: } catch (NamingException x) {
206: if (logger.isEnabledFor(Logger.DEBUG))
207: logger.debug("Cannot close InitialContext", x);
208: }
209: }
210: }
211: }
212:
213: protected String encodeStub(RMIServerImpl rmiServer, Map environment)
214: throws IOException {
215: Remote stub = rmiServer.toStub();
216: ByteArrayOutputStream baos = new ByteArrayOutputStream();
217: ObjectOutputStream oos = null;
218: try {
219: oos = new ObjectOutputStream(baos);
220: oos.writeObject(stub);
221: } finally {
222: if (oos != null)
223: oos.close();
224: }
225: byte[] bytes = baos.toByteArray();
226: byte[] encoded = Base64Codec.encodeBase64(bytes);
227: // Since the bytes are base 64 bytes, the encoding in creating the string is not important: any will work
228: return STUB_CONTEXT + new String(encoded);
229: }
230:
231: protected boolean isEncodedForm(JMXServiceURL url) {
232: String path = url.getURLPath();
233: if (path == null || path.length() == 0 || path.equals("/")
234: || path.startsWith(STUB_CONTEXT))
235: return true;
236: return false;
237: }
238:
239: private String parseJNDIForm(JMXServiceURL url)
240: throws MalformedURLException {
241: String path = url.getURLPath();
242: if (path.startsWith(JNDI_CONTEXT)) {
243: String jndiURL = path.substring(JNDI_CONTEXT.length());
244: if (jndiURL == null || jndiURL.length() == 0)
245: throw new MalformedURLException(
246: "No JNDI URL specified: " + url);
247: return jndiURL;
248: }
249: throw new MalformedURLException("Unsupported binding: " + url);
250: }
251:
252: public void unbindServer(Object server, JMXServiceURL url,
253: Map environment) throws IOException {
254: Logger logger = getLogger();
255: if (logger.isEnabledFor(Logger.DEBUG))
256: logger.debug("JMXServiceURL for unbinding is: '" + url
257: + "'");
258:
259: // The server was not bound to JNDI (the stub was encoded), just return
260: if (isEncodedForm(url)) {
261: return;
262: } else {
263: String jndiURL = parseJNDIForm(url);
264: if (logger.isEnabledFor(Logger.DEBUG))
265: logger.debug("JMXServiceURL path for binding is: '"
266: + jndiURL + "'");
267:
268: InitialContext ctx = null;
269: try {
270: ctx = new InitialContext(new Hashtable(environment));
271: ctx.unbind(jndiURL);
272: if (logger.isEnabledFor(Logger.DEBUG))
273: logger.debug("Unbound " + server + " from "
274: + jndiURL);
275: } catch (NamingException x) {
276: if (logger.isEnabledFor(Logger.DEBUG))
277: logger.debug("Cannot unbind server " + server
278: + " to " + jndiURL, x);
279: throw new IOException(x.toString());
280: } finally {
281: try {
282: if (ctx != null)
283: ctx.close();
284: } catch (NamingException x) {
285: if (logger.isEnabledFor(Logger.DEBUG))
286: logger.debug("Cannot close InitialContext", x);
287: }
288: }
289: }
290: }
291:
292: public void destroyServer(Object server, JMXServiceURL url,
293: Map environment) throws IOException {
294: }
295: }
|