RmiConnector.java revision 86548277ecc6d1ad5fa9126c63b44bdd24666e07
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.jmx;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLContext;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import org.opends.server.api.KeyManagerProvider;
import org.opends.server.config.JMXMBean;
import org.opends.server.core.DirectoryServer;
import org.opends.server.extensions.NullKeyManagerProvider;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.debugVerbose;
import static org.opends.server.loggers.debug.DebugLogger.debugWarning;
import static org.opends.server.loggers.debug.DebugLogger.debugError;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.util.SelectableCertificateKeyManager;
/**
* The RMI connector class starts and stops the JMX RMI connector server.
* There are 2 different connector servers
* <ul>
* <li> the RMI Client connector server, supporting TLS-encrypted.
* communication, server authentication by certificate and client
* authentication by providing appropriate LDAP credentials through
* SASL/PLAIN.
* <li> the RMI client connector server, supporting TLS-encrypted
* communication, server authentication by certificate, client
* authentication by certificate and identity assertion through SASL/PLAIN.
* </ul>
* <p>
* Each connector is registered into the JMX MBean server.
*/
public class RmiConnector
{
/**
* The MBean server used to handle JMX interaction.
*/
private MBeanServer mbs = null;
/**
* the client address to connect to the common registry. Note that a
* remote client should use the correct IP address.
*/
private String registryClientAddress = "0.0.0.0";
/**
* The associated JMX Connection Handler.
*/
private JmxConnectionHandler jmxConnectionHandler;
/**
* The name of the JMX connector with no SSL client
* authentication.
*/
private String jmxRmiConnectorNoClientCertificateName;
/**
* The name of the JMX connector with SSL client
* authentication.
*/
private String jmxRmiConnectorClientCertificateName;
/**
* The reference to the JMX connector client with no SSL client
* authentication.
*/
protected JMXConnectorServer jmxRmiConnectorNoClientCertificate;
/**
* The reference to the JMX connector client with SSL client
* authentication.
*/
private JMXConnectorServer jmxRmiConnectorClientCertificate;
/**
* The reference to authenticator.
*/
private RmiAuthenticator rmiAuthenticator;
/**
* The reference to the created RMI registry.
*/
private Registry registry = null;
/**
* The Underlying Socket factory.
*/
private OpendsRmiServerSocketFactory rmiSsf;
/**
* The RMI protocol verison used by this connector.
*/
private String rmiVersion;
// ===================================================================
// CONSTRUCTOR
// ===================================================================
/**
* Create a new instance of RmiConnector .
*
* @param mbs
* The MBean server.
* @param jmxConnectionHandler
* The associated JMX Connection Handler
*/
public RmiConnector(MBeanServer mbs,
JmxConnectionHandler jmxConnectionHandler)
{
this.mbs = mbs;
this.jmxConnectionHandler = jmxConnectionHandler;
String baseName = JMXMBean.getJmxName(jmxConnectionHandler
.getComponentEntryDN());
jmxRmiConnectorNoClientCertificateName = baseName + ","
+ "Type=jmxRmiConnectorNoClientCertificateName";
jmxRmiConnectorClientCertificateName = baseName + ","
+ "Type=jmxRmiConnectorClientCertificateName";
}
// ===================================================================
// Initialization
// ===================================================================
/**
* Activates the RMI Connectors. It starts the secure connectors.
*/
public void initialize()
{
try
{
//
// start the common registry
startCommonRegistry();
//
// start the RMI connector (SSL + server authentication)
startConnectorNoClientCertificate();
//
// start the RMI connector (SSL + server authentication +
// client authentication + identity given part SASL/PLAIN)
// TODO startConnectorClientCertificate(clientConnection);
}
catch (Exception e)
{
if (debugEnabled())
{
debugCaught(DebugLogLevel.ERROR, e);
}
throw new RuntimeException("Error while starting the RMI module : "
+ e.getMessage());
}
if (debugEnabled())
{
debugVerbose("RMI module started");
}
}
/**
* Starts the common RMI registry. In order to provide RMI stub for
* remote client, the JMX RMI connector should be register into an RMI
* registry. Each server will maintain its own private one.
*
* @throws Exception
* if the registry cannot be started
*/
private void startCommonRegistry() throws Exception
{
int registryPort = jmxConnectionHandler.getListenPort();
//
// create our local RMI registry if it does not exist already
if (debugEnabled())
{
debugVerbose("start or reach an RMI registry on port %d", registryPort);
}
try
{
//
// TODO Not yet implemented: If the host has several interfaces
if (registry == null)
{
rmiSsf = new OpendsRmiServerSocketFactory();
registry = LocateRegistry.createRegistry(registryPort, null, rmiSsf);
}
}
catch (RemoteException re)
{
//
// is the registry already created ?
if (debugEnabled())
{
debugWarning("cannot create the RMI registry -> already done ?");
}
try
{
//
// get a 'remote' reference on the registry
Registry reg = LocateRegistry.getRegistry(registryPort);
//
// 'ping' the registry
reg.list();
registry = reg;
}
catch (Exception e)
{
if (debugEnabled())
{
//
// no 'valid' registry found on the specified port
debugError("exception thrown while pinging the RMI registry");
//
// throw the original exception
debugCaught(DebugLogLevel.ERROR, re);
}
throw re;
}
//
// here the registry is ok even though
// it was not created by this call
if (debugEnabled())
{
debugWarning("RMI was registry already started");
}
}
}
/**
* Starts a secure RMI connector, with a client that doesn't have to
* present a certificate, on the local mbean server.
* This method assumes that the common registry was successfully
* started.
* <p>
* If the connector is already started, this method simply returns
* without doing anything.
*
* @throws Exception
* if an error occurs
*/
private void startConnectorNoClientCertificate() throws Exception
{
try
{
//
// Environment map
HashMap<String, Object> env = new HashMap<String, Object>();
// ---------------------
// init an ssl context
// ---------------------
DirectoryRMIClientSocketFactory rmiClientSockeyFactory = null;
DirectoryRMIServerSocketFactory rmiServerSockeyFactory = null;
if (jmxConnectionHandler.isUseSSL())
{
if (debugEnabled())
{
debugVerbose("SSL connection");
}
// ---------------------
// SERVER SIDE
// ---------------------
//
// Get a Server socket factory
KeyManager[] keyManagers;
KeyManagerProvider provider = DirectoryServer
.getKeyManagerProvider(jmxConnectionHandler
.getKeyManagerProviderDN());
if (provider == null) {
keyManagers = new NullKeyManagerProvider().getKeyManagers();
}
else
{
String nickname = jmxConnectionHandler.getSSLServerCertNickname();
if (nickname == null)
{
keyManagers = provider.getKeyManagers();
}
else
{
keyManagers =
SelectableCertificateKeyManager.wrap(provider.getKeyManagers(),
nickname);
}
}
SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(
keyManagers,
null,
null);
SSLSocketFactory ssf = ctx.getSocketFactory();
//
// set the Server socket factory in the JMX map
rmiServerSockeyFactory = new DirectoryRMIServerSocketFactory(ssf,
false);
env.put(
"jmx.remote.rmi.server.socket.factory",
rmiServerSockeyFactory);
// ---------------------
// CLIENT SIDE : Rmi stores the client stub in the
// registry
// ---------------------
// Set the Client socket factory in the JMX map
rmiClientSockeyFactory = new DirectoryRMIClientSocketFactory(false);
env.put(
"jmx.remote.rmi.client.socket.factory",
rmiClientSockeyFactory);
}
else
{
if (debugEnabled())
{
debugVerbose("UNSECURE CONNECTION");
}
}
//
// specify the rmi JMX authenticator to be used
if (debugEnabled())
{
debugVerbose("Add RmiAuthenticator into JMX map");
}
rmiAuthenticator = new RmiAuthenticator(jmxConnectionHandler);
env.put(JMXConnectorServer.AUTHENTICATOR, rmiAuthenticator);
//
// Create the JMX Service URL
String uri = "org.opends.server.protocols.jmx.client-unknown";
String serviceUrl = "service:jmx:rmi:///jndi/rmi://"
+ registryClientAddress + ":" + jmxConnectionHandler.getListenPort()
+ "/" + uri;
JMXServiceURL url = new JMXServiceURL(serviceUrl);
//
// Create and start the connector
if (debugEnabled())
{
debugVerbose("Create and start the JMX RMI connector");
}
OpendsRMIJRMPServerImpl opendsRmiConnectorServer =
new OpendsRMIJRMPServerImpl(
0, rmiClientSockeyFactory, rmiServerSockeyFactory, env);
jmxRmiConnectorNoClientCertificate = new RMIConnectorServer(url, env,
opendsRmiConnectorServer, mbs);
jmxRmiConnectorNoClientCertificate.start();
//
// Register the connector into the RMI registry
// TODO Should we do that?
ObjectName name = new ObjectName(jmxRmiConnectorNoClientCertificateName);
mbs.registerMBean(jmxRmiConnectorNoClientCertificate, name);
rmiVersion = opendsRmiConnectorServer.getVersion();
if (debugEnabled())
{
debugVerbose("JMX RMI connector Started");
}
}
catch (Exception e)
{
if (debugEnabled())
{
debugCaught(DebugLogLevel.ERROR, e);
}
throw e;
}
}
/**
* Closes this connection handler so that it will no longer accept new
* client connections. It may or may not disconnect existing client
* connections based on the provided flag.
*
* @param closeConnections
* Indicates whether any established client connections
* associated with the connection handler should also be
* closed.
* @param stopRegistry Indicates if the RMI registry should be stopped
*/
public void finalizeConnectionHandler(
boolean closeConnections, boolean stopRegistry)
{
if (closeConnections)
{
try
{
if (jmxRmiConnectorNoClientCertificate != null)
{
jmxRmiConnectorNoClientCertificate.stop();
}
if (jmxRmiConnectorClientCertificate != null)
{
jmxRmiConnectorClientCertificate.stop();
}
}
catch (Exception e)
{
}
jmxRmiConnectorNoClientCertificate = null;
jmxRmiConnectorClientCertificate = null;
}
else
{
rmiAuthenticator.setFinalizedPhase(true);
}
//
// Unregister connectors and stop them.
try
{
ObjectName name = new ObjectName(jmxRmiConnectorNoClientCertificateName);
if (mbs.isRegistered(name))
{
mbs.unregisterMBean(name);
}
if (jmxRmiConnectorNoClientCertificate != null)
{
jmxRmiConnectorNoClientCertificate.stop();
}
// TODO: unregister the connector with SSL client authen
// name = new ObjectName(jmxRmiConnectorClientCertificateName);
// if (mbs.isRegistered(name))
// {
// mbs.unregisterMBean(name);
// }
// jmxRmiConnectorClientCertificate.stop() ;
}
catch (Exception e)
{
// TODO Log an error message
if (debugEnabled())
{
debugCaught(DebugLogLevel.ERROR, e);
}
}
if (stopRegistry)
{
//
// Close the socket
try
{
rmiSsf.close();
}
catch (IOException e)
{
// TODO Log an error message
if (debugEnabled())
{
debugCaught(DebugLogLevel.ERROR, e);
}
}
registry = null;
}
}
/**
* Retrieves the RMI protocol version string in use for this connector.
*
* @return The RMI protocol version string in use for this connector.
*/
public String getProtocolVersion()
{
return rmiVersion;
}
}