/*
 * Copyright (C) The MX4J Contributors.
 * 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.
 */

package mx4j.examples.remote.rmi.ssl;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;

import mx4j.tools.remote.rmi.SSLRMIClientSocketFactory;
import mx4j.tools.remote.rmi.SSLRMIServerSocketFactory;

/**
 * This example shows how to setup a JSR 160 RMIConnectorServer over SSL. <br />
 * An RMI server that has been setup to use SSL uses a private key to encrypt the
 * communication with the client. The client must know the server's public key in order
 * to be able to decrypt the communication; public keys are stored in X509 certificates.
 * This X509 certificate is generated by the server and should be made available to
 * clients (for example by distributing it). <br />
 * The private and public key are normally stored in a server-side key store that can
 * be created by using the JDK's keytool utility; here is a sample command that can
 * be invoked to generate a keystore:
 * <pre>
 * keytool -genkey -v -keystore key.store -storepass storepwd -dname "CN=Anonymous Geek, OU=MX4J Development Team, O=The MX4J Project, L=New York City, S=NY, C=US"
 * </pre>
 * It creates a 'key.store' file that must be present in the classpath when running this example. <br />
 * The next step is to export the X509 certificate for the clients, with the following command:
 * <pre>
 * keytool -export -v -storepass storepwd -keystore key.store -file myserver.cer
 * </pre>
 * It is also possible to generate a trust store containing the X509 certificate that
 * can be used directly by the client with the following command:
 * <pre>
 * keytool -export -v -storepass storepwd -keystore key.store | keytool -import -v -storepass storepwd -keystore trust.store -noprompt
 * </pre>
 * Once you have exported the X509 certificate, follow the instructions on how to setup
 * the client {@link Client here}.
 *
 * @version $Revision: 1.4 $
 */
public class Server
{
   public static void main(String[] args) throws Exception
   {
      MBeanServer server = MBeanServerFactory.createMBeanServer();

      // Register and start the rmiregistry MBean
      ObjectName namingName = ObjectName.getInstance("naming:type=rmiregistry");
      server.createMBean("mx4j.tools.naming.NamingService", namingName, null);
      server.invoke(namingName, "start", null, null);
      int namingPort = ((Integer)server.getAttribute(namingName, "Port")).intValue();

      String jndiPath = "/ssljmxconnector";
      JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:" + namingPort + jndiPath);

      // Create the rmi socket factories for SSL
      Map environment = new HashMap();
      SSLContext context = createSSLContext();
      environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, new SSLRMIClientSocketFactory());
      environment.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, new SSLRMIServerSocketFactory(context));

      // Create and start the RMIConnectorServer
      JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, environment, null);
      ObjectName connectorServerName = ObjectName.getInstance("connectors:protocol=" + url.getProtocol());
      server.registerMBean(connectorServer, connectorServerName);
      connectorServer.start();

      System.out.println("Server up and running");
   }

   /**
    * Creates and returns an SSLContext by reading information from a keystore. <br>
    * Change the hardcoded options to match your configuration and your environment.
    */
   private static SSLContext createSSLContext() throws Exception
   {
      String keystoreName = "key.store";
      String keystorePassword = "storepwd";

      KeyStore keystore = KeyStore.getInstance("JKS");
      InputStream keystoreStream = Server.class.getClassLoader().getResourceAsStream(keystoreName);
      // Must check for nullity, otherwise a new empty keystore is created by KeyStore.load
      if (keystoreStream == null) throw new IOException("Cannot find KeyStore " + keystoreName + " in classpath");
      keystore.load(keystoreStream, keystorePassword.toCharArray());

      KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("SunX509");
      keyFactory.init(keystore, keystorePassword.toCharArray());

      SSLContext context = SSLContext.getInstance("TLS");
      context.init(keyFactory.getKeyManagers(), null, null);

      return context;
   }
}
