blob: cfcbbea7492a3c4bac348309db5aa8febdd69e72 [file] [log] [blame]
/*
* 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
{
}
}