| /* |
| * Copyright (C) MX4J. |
| * All rights reserved. |
| * |
| * This software is distributed under the terms of the MX4J License version 1.0. |
| * See the terms of the MX4J License in the documentation provided with this software. |
| */ |
| /* |
| * Copyright 2005 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.resolver.rmi; |
| |
| import java.io.IOException; |
| import java.io.ByteArrayOutputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.ObjectInputStream; |
| import java.net.MalformedURLException; |
| import java.rmi.server.RMIClientSocketFactory; |
| import java.rmi.server.RMIServerSocketFactory; |
| import java.rmi.Remote; |
| import java.util.Hashtable; |
| import java.util.Map; |
| |
| import javax.management.remote.JMXServiceURL; |
| import javax.management.remote.rmi.RMIConnectorServer; |
| import javax.management.remote.rmi.RMIJRMPServerImpl; |
| import javax.management.remote.rmi.RMIServer; |
| import javax.management.remote.rmi.RMIServerImpl; |
| import javax.naming.InitialContext; |
| import javax.naming.NamingException; |
| |
| import org.osgi.service.log.LogService; |
| |
| import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator; |
| |
| import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ConnectionResolver; |
| import org.apache.felix.mosgi.jmx.agent.mx4j.util.Base64Codec; |
| |
| /** |
| * Resolver for RMI/JRMP protocol. |
| * |
| * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a> |
| * @version $Revision: 1.1.1.1 $ |
| */ |
| public class RMIResolver extends ConnectionResolver |
| { |
| private static final String JNDI_CONTEXT = "/jndi/"; |
| private static final String STUB_CONTEXT = "/stub/"; |
| |
| |
| //********************************************************************************************************************// |
| // CLIENT METHODS |
| |
| |
| public Object lookupClient(JMXServiceURL url, Map environment) throws IOException |
| { |
| return lookupRMIServerStub(url, environment); |
| } |
| |
| public Object bindClient(Object client, Map environment) throws IOException |
| { |
| // JRMP does not need anything special |
| return client; |
| } |
| |
| protected RMIServer lookupRMIServerStub(JMXServiceURL url, Map environment) throws IOException |
| { |
| String path = url.getURLPath(); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL for lookup is: '" + url + "'", null); |
| |
| if (path != null) |
| { |
| if (path.startsWith(JNDI_CONTEXT)) |
| { |
| return lookupStubInJNDI(url, environment); |
| } |
| |
| return decodeStub(url, environment); |
| } |
| |
| throw new MalformedURLException("Unsupported lookup " + url); |
| } |
| |
| private RMIServer lookupStubInJNDI(JMXServiceURL url, Map environment) throws IOException |
| { |
| |
| String path = url.getURLPath(); |
| String name = path.substring(JNDI_CONTEXT.length()); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Looking up RMI stub in JNDI under name " + name, null); |
| |
| InitialContext ctx = null; |
| try |
| { |
| ctx = new InitialContext(new Hashtable(environment)); |
| Object stub = ctx.lookup(name); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Found RMI stub in JNDI " + stub, null); |
| return narrowRMIServerStub(stub); |
| } |
| catch (NamingException x) |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot lookup RMI stub in JNDI", x); |
| throw new IOException(x.toString()); |
| } |
| finally |
| { |
| try |
| { |
| if (ctx != null) ctx.close(); |
| } |
| catch (NamingException x) |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot close InitialContext", x); |
| } |
| } |
| } |
| |
| protected RMIServer narrowRMIServerStub(Object stub) |
| { |
| return (RMIServer)stub; |
| } |
| |
| protected RMIServer decodeStub(JMXServiceURL url, Map environment) throws IOException |
| { |
| String path = url.getURLPath(); |
| if (path.startsWith(STUB_CONTEXT)) |
| { |
| byte[] encoded = path.substring(STUB_CONTEXT.length()).getBytes(); |
| if (!Base64Codec.isArrayByteBase64(encoded)) throw new IOException("Encoded stub form is not a valid Base64 sequence: " + url); |
| byte[] decoded = Base64Codec.decodeBase64(encoded); |
| ByteArrayInputStream bais = new ByteArrayInputStream(decoded); |
| ObjectInputStream ois = null; |
| try |
| { |
| ois = new ObjectInputStream(bais); |
| return (RMIServer)ois.readObject(); |
| } |
| catch (ClassNotFoundException x) |
| { |
| throw new IOException("Cannot decode stub from " + url + ": " + x); |
| } |
| finally |
| { |
| if (ois != null) ois.close(); |
| } |
| } |
| throw new MalformedURLException("Unsupported binding: " + url); |
| } |
| |
| |
| //********************************************************************************************************************// |
| // SERVER METHODS |
| |
| |
| public Object createServer(JMXServiceURL url, Map environment) throws IOException |
| { |
| return createRMIServer(url, environment); |
| } |
| |
| protected RMIServerImpl createRMIServer(JMXServiceURL url, Map environment) throws IOException |
| { |
| int port = url.getPort(); |
| RMIClientSocketFactory clientFactory = (RMIClientSocketFactory)environment.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE); |
| RMIServerSocketFactory serverFactory = (RMIServerSocketFactory)environment.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE); |
| return new RMIJRMPServerImpl(port, clientFactory, serverFactory, environment); |
| } |
| |
| public JMXServiceURL bindServer(Object server, JMXServiceURL url, Map environment) throws IOException |
| { |
| // See javax/management/remote/rmi/package-summary.html |
| |
| RMIServerImpl rmiServer = (RMIServerImpl)server; |
| |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL for binding is: '" + url + "'",null); |
| |
| if (isEncodedForm(url)) |
| { |
| String path = encodeStub(rmiServer, environment); |
| return new JMXServiceURL(url.getProtocol(), url.getHost(), url.getPort(), path); |
| } |
| |
| String jndiURL = parseJNDIForm(url); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL path for binding is: '" + jndiURL + "'", null); |
| |
| InitialContext ctx = null; |
| try |
| { |
| ctx = new InitialContext(new Hashtable(environment)); |
| boolean rebind = Boolean.valueOf((String)environment.get(RMIConnectorServer.JNDI_REBIND_ATTRIBUTE)).booleanValue(); |
| if (rebind) |
| ctx.rebind(jndiURL, rmiServer.toStub()); |
| else |
| ctx.bind(jndiURL, rmiServer.toStub()); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Bound " + rmiServer + " to " + jndiURL, null); |
| return url; |
| } |
| catch (NamingException x) |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot bind server " + rmiServer + " to " + jndiURL, x); |
| throw new IOException(x.toString()); |
| } |
| finally |
| { |
| try |
| { |
| if (ctx != null) ctx.close(); |
| } |
| catch (NamingException x) |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot close InitialContext", x); |
| } |
| } |
| } |
| |
| protected String encodeStub(RMIServerImpl rmiServer, Map environment) throws IOException |
| { |
| Remote stub = rmiServer.toStub(); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ObjectOutputStream oos = null; |
| try |
| { |
| oos = new ObjectOutputStream(baos); |
| oos.writeObject(stub); |
| } |
| finally |
| { |
| if (oos != null) oos.close(); |
| } |
| byte[] bytes = baos.toByteArray(); |
| byte[] encoded = Base64Codec.encodeBase64(bytes); |
| // Since the bytes are base 64 bytes, the encoding in creating the string is not important: any will work |
| return STUB_CONTEXT + new String(encoded); |
| } |
| |
| protected boolean isEncodedForm(JMXServiceURL url) |
| { |
| String path = url.getURLPath(); |
| if (path == null || path.length() == 0 || path.equals("/") || path.startsWith(STUB_CONTEXT)) return true; |
| return false; |
| } |
| |
| private String parseJNDIForm(JMXServiceURL url) throws MalformedURLException |
| { |
| String path = url.getURLPath(); |
| if (path.startsWith(JNDI_CONTEXT)) |
| { |
| String jndiURL = path.substring(JNDI_CONTEXT.length()); |
| if (jndiURL == null || jndiURL.length() == 0) throw new MalformedURLException("No JNDI URL specified: " + url); |
| return jndiURL; |
| } |
| throw new MalformedURLException("Unsupported binding: " + url); |
| } |
| |
| public void unbindServer(Object server, JMXServiceURL url, Map environment) throws IOException |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL for unbinding is: '" + url + "'", null); |
| // The server was not bound to JNDI (the stub was encoded), just return |
| if (isEncodedForm(url)) |
| { |
| destroyServer(server, environment); |
| return; |
| } |
| |
| String jndiURL = parseJNDIForm(url); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL path for binding is: '" + jndiURL + "'",null); |
| |
| InitialContext ctx = null; |
| try |
| { |
| ctx = new InitialContext(new Hashtable(environment)); |
| ctx.unbind(jndiURL); |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Unbound " + server + " from " + jndiURL, null); |
| } |
| catch (NamingException x) |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot unbind server " + server + " to " + jndiURL, x); |
| throw new IOException(x.toString()); |
| } |
| finally |
| { |
| try |
| { |
| if (ctx != null) ctx.close(); |
| } |
| catch (NamingException x) |
| { |
| RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot close InitialContext", x); |
| } |
| } |
| } |
| |
| protected void destroyServer(Object server, Map environment) throws IOException |
| { |
| } |
| } |