All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.impl.admin.AdminService Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

The newest version!
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.admin;

import static oracle.kv.util.http.Constants.ADMIN_PATH_NAME;

import java.net.InetSocketAddress;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.KeyManagerFactory;

import oracle.kv.impl.admin.param.AdminParams;
import oracle.kv.impl.admin.param.BootstrapParams;
import oracle.kv.impl.admin.param.GlobalParams;
import oracle.kv.impl.admin.param.SecurityParams;
import oracle.kv.impl.admin.param.StorageNodeParams;
import oracle.kv.impl.admin.plan.PlanProgress;
import oracle.kv.impl.admin.plan.PlanStateChange;
import oracle.kv.impl.admin.web.AdminWebService;
import oracle.kv.impl.client.admin.ClientAdminService;
import oracle.kv.impl.fault.ProcessFaultHandler;
import oracle.kv.impl.measurement.ServiceStatusChange;
import oracle.kv.impl.mgmt.AdminStatusReceiver;
import oracle.kv.impl.monitor.ViewListener;
import oracle.kv.impl.param.LoadParameters;
import oracle.kv.impl.param.ParameterListener;
import oracle.kv.impl.param.ParameterMap;
import oracle.kv.impl.param.ParameterState;
import oracle.kv.impl.param.ParameterUtils;
import oracle.kv.impl.security.ConfigurationException;
import oracle.kv.impl.security.RoleResolver;
import oracle.kv.impl.security.SecureProxy;
import oracle.kv.impl.security.login.InternalLoginManager;
import oracle.kv.impl.security.login.UserLogin;
import oracle.kv.impl.security.util.SecurityUtils;
import oracle.kv.impl.topo.AdminId;
import oracle.kv.impl.topo.AdminType;
import oracle.kv.impl.topo.ResourceId;
import oracle.kv.impl.topo.StorageNodeId;
import oracle.kv.impl.util.ConfigurableService;
import oracle.kv.impl.util.FileNames;
import oracle.kv.impl.util.PortRange;
import oracle.kv.impl.util.registry.AsyncControl;
import oracle.kv.impl.util.registry.AsyncRegistryUtils;
import oracle.kv.impl.util.registry.RMISocketPolicy;
import oracle.kv.impl.util.registry.RMISocketPolicy.SocketFactoryPair;
import oracle.kv.impl.util.registry.RegistryUtils;
import oracle.kv.impl.util.server.LoggerUtils;
import oracle.kv.impl.util.sklogger.SkLogger;
import oracle.kv.util.http.HttpServer;
import oracle.kv.util.http.LogControl;
import oracle.kv.util.http.ProxyRequestHandler;

import com.sleepycat.je.rep.ReplicatedEnvironment.State;
import com.sleepycat.je.rep.ReplicationGroup;
import com.sleepycat.je.rep.ReplicationNetworkConfig;
import com.sleepycat.je.rep.util.ReplicationGroupAdmin;
import com.sleepycat.je.rep.utilint.HostPortPair;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

/**
 * AdminService houses the two services provided for administering a kv store
 * instance and providing clients with access to Admin API functions.
 */
public class AdminService implements ConfigurableService {

    private AdminServiceParams params;
    private Admin admin = null;           /* Admin can be null at bootstrap. */
    private CommandService commandService;
    private CommandService exportableCommandService;
    private LoginService loginService;
    private UserLogin exportableUL;
    private ClientAdminService clientService;
    private ClientAdminService exportableClientService;
    private HttpServer webServer;
    private AdminWebService adminWebService;
    private AdminStatusReceiver statusReceiver = null;
    private ParameterListener parameterListener = null;
    private MgmtAgentPlanStateChangeTracker planStateListener = null;
    private MgmtAgentPlanProgressTracker planProgressListener = null;
    private AdminSecurity adminSecurity;

    private Logger logger;

    private AdminServiceFaultHandler faultHandler;
    private final boolean usingThreads;

    public static final int ADMIN_MIN_HEAP_MB = ParameterUtils.applyMinHeapMB(96);
    public static final int ADMIN_MAX_HEAP_MB = ParameterUtils.applyMinHeapMB(128);

    /* Default Java heap to 96M for now */
    public static final String DEFAULT_JAVA_ARGS =
        "-XX:+DisableExplicitGC " +
        "-Xms" + ADMIN_MIN_HEAP_MB + "M -Xmx" + ADMIN_MAX_HEAP_MB + "M " +
        "-server" +

        /*
         * Disable JE's requirement that helper host names be resolvable.  We
         * want nodes to be able to start up even if other nodes in the
         * replication group have been removed and no longer have DNS names.
         * [#23120]
         */
        " -Dje.rep.skipHelperHostResolution=true";

    /* For unit test support, to determine if the service is up or down. */
    private boolean active = false;

    /**
     * Whether the stop method has been called, even if the actual stop is
     * being delayed because of other operations.
     */
    private volatile boolean stopRequested;

    /**
     * Creates a non-bootstrap AdminService.  The initialize() method must
     * be called before start().
     */
    public AdminService(boolean usingThreads) {
        this.usingThreads = usingThreads;
        faultHandler = null;
    }

    /**
     * Creates a bootstrap AdminService.  The initialize() method should not
     * be called before start(). The bootstrap service is always created as
     * a PRIMARY Admin.
     */
    public AdminService(BootstrapParams bp,
                        SecurityParams sp,
                        boolean usingThreads) {
        this.usingThreads = usingThreads;
        deriveASParams(bp, sp);

        /*
         * No need to worry about RMI Socket Policies and Registry CSF here
         * because ManagedBootstrapAdmin takes care of that for us, if needed.
         */

        /* When kvStoreName is null, we are starting in bootstrap mode. */
        if (params.getGlobalParams().getKVStoreName() == null) {
            logger =
                LoggerUtils.getBootstrapLogger(bp.getRootdir(),
                                               FileNames.BOOTSTRAP_ADMIN_LOG,
                                               "BootstrapAdmin");
        } else {
            logger = LoggerUtils.getLogger(this.getClass(), params);
        }
        faultHandler = new AdminServiceFaultHandler(logger, this);
        adminSecurity = new AdminSecurity(this, logger);
    }

    /**
     * Initialize an AdminService.  This must be called before start() if not
     * created via the BootStrap constructor.
     */
    public void initialize(SecurityParams securityParams,
                           AdminParams adminParams,
                           LoadParameters lp) {

        securityParams.initRMISocketPolicies();

        GlobalParams globalParams =
            new GlobalParams(lp.getMap(ParameterState.GLOBAL_TYPE));

        noteKVStoreName(globalParams.getKVStoreName());

        StorageNodeParams storageNodeParams =
            new StorageNodeParams(lp.getMap(ParameterState.SNA_TYPE));
        /*
         * Initializing storage node params with admin mount point before
         * start. If admin mount is not specified then kvroot directory will
         * host admin env files as per current design.
         *
         * TODO: Need to check if admin directory should be part of AdminParams
         * like RN Log Directories are part of RepNodeParams.
         */
        storageNodeParams.setAdminDirMap(
            lp.getMap(ParameterState.BOOTSTRAP_ADMIN_MOUNT_POINTS));

        params = new AdminServiceParams
            (securityParams, globalParams, storageNodeParams, adminParams);

        logger = LoggerUtils.getLogger(this.getClass(), params);
        if (faultHandler == null) {
            faultHandler = new AdminServiceFaultHandler(logger, this);
        }

        if (!usingThreads) {
            storageNodeParams.setRegistryCSF(securityParams);
        }

        adminSecurity = new AdminSecurity(this, logger);
    }

    /**
     * Start and stop are synchronized to avoid having stop() run before
     * start() is done.  This comes up in testing.
     */
    @Override
    public synchronized void start() {
        getFaultHandler().execute
            (new ProcessFaultHandler.SimpleProcedure() {

                @Override
                public void execute() {
                    startInternal();
                }
             });
    }

    private void startInternal() {

        /*
         * Disable support for async requests on the server side in process
         * mode since the admin service doesn't need this capability.
         */
        if (!usingThreads) {
            AsyncControl.serverUseAsync = false;
        }

        final StorageNodeParams snParams = params.getStorageNodeParams();
        final String hostName = snParams.getHostname();

        /* Sets the server hostname for remote objects */
        AsyncRegistryUtils.setServerHostName(hostName);

        /* Disable to allow for faster timeouts on failed connections. */
        System.setProperty("java.rmi.server.disableHttp", "true");

        /*
         * If kvStoreName is null, then we are starting in bootstrap mode.
         * The Admin can't be created yet.
         */
        final String kvStoreName = params.getGlobalParams().getKVStoreName();
        if (kvStoreName == null) {
            logger.info("Starting in bootstrap mode");
        } else {
            logger.info("Starting AdminService");
            admin = new Admin(params, this);
        }

        /*
         * Create the UserLoginImpl instance and bind it in the registry if
         * security is enabled.
         */
        final boolean isSecure = params.getSecurityParams().isSecure();
        if (isSecure) {

            ServiceBinder serviceBinder =
                new ServiceBinder
                (GlobalParams.ADMIN_LOGIN_SERVICE_NAME, this) {

                @Override
                LoginService makeInsecureService() {
                    return new LoginService(aservice);
                }

                @Override
                UserLogin getRemote() {
                    return insecureService.getUserLogin();
                }
            };

            loginService = serviceBinder.getInsecureService();
            exportableUL = serviceBinder.getSecureService();

            /* Install the login updater now */
            if (admin != null) {
                admin.installSecurityUpdater();
            }
        }

        /* Create the CommandService instance and bind it in the registry */
        ServiceBinder serviceBinder =
            new ServiceBinder
            (GlobalParams.COMMAND_SERVICE_NAME, this) {

            @Override
            CommandServiceImpl makeInsecureService() {
                return new CommandServiceImpl(aservice);
            }

            @Override
            CommandService getRemote() {
                return insecureService;
            }
        };
        commandService = serviceBinder.getInsecureService();
        exportableCommandService = serviceBinder.getSecureService();

        /* Create the ClientAdminService for handling DDL and DML statements */
        ServiceBinder stmtBinder =
            new ServiceBinder
            (GlobalParams.CLIENT_ADMIN_SERVICE_NAME, this) {

            @Override
            ClientAdminServiceImpl makeInsecureService() {
                return new ClientAdminServiceImpl(aservice, logger);
            }

            @Override
            ClientAdminService getRemote() {
                return insecureService;
            }
        };
        clientService = stmtBinder.getInsecureService();
        exportableClientService = stmtBinder.getSecureService();

        startWebService();

        synchronized (this) {
            active = true;
            this.notifyAll();
        }
        logger.info("Started AdminService");
    }

    @Override
    public void stop(boolean force) {
        stopRequested = true;
        synchronized (this) {
            logger.info("Shutting down AdminService instance" +
                        (force ? " (force)" : ""));

            update(ServiceStatus.STOPPING);
            String hostName = params.getStorageNodeParams().getHostname();

            if (webServer != null) {
                try {
                    adminWebService.shutdown();
                    webServer.shutdown();
                    adminWebService = null;
                    webServer = null;
                } catch (Exception e) {
                    final String message = "Can't shutdown admin web server";
                    logger.severe(message);
                    throw new IllegalStateException(message, e);
                }
            }

            if (commandService != null) {
                ((CommandServiceImpl) commandService).stopRemoteTestInterface(
                    logger);
                try {
                    logger.info("Unbinding CommandService");
                    RegistryUtils.unbind
                        (hostName,
                         params.getStorageNodeParams().getRegistryPort(),
                         GlobalParams.COMMAND_SERVICE_NAME,
                         exportableCommandService);
                    commandService = null;
                } catch (RemoteException re) {
                    String msg = "Can't unbind CommandService. ";
                    logger.severe(msg + LoggerUtils.getStackTrace(re));
                    throw new IllegalStateException(msg, re);
                }
            }

            if (clientService != null) {
                try {
                    logger.info("Unbinding ClientAdminService");
                    RegistryUtils.unbind
                        (hostName,
                         params.getStorageNodeParams().getRegistryPort(),
                         GlobalParams.CLIENT_ADMIN_SERVICE_NAME,
                     exportableClientService);
                    clientService = null;
                } catch (RemoteException re) {
                    String msg = "Can't unbind ClientAdminService. ";
                    logger.severe(msg + LoggerUtils.getStackTrace(re));
                    throw new IllegalStateException(msg, re);
                }
            }

            if (loginService != null) {
                try {
                    logger.info("Unbinding LoginService");
                    RegistryUtils.unbind
                        (hostName,
                         params.getStorageNodeParams().getRegistryPort(),
                         GlobalParams.ADMIN_LOGIN_SERVICE_NAME,
                         exportableUL);
                    loginService = null;
                } catch (RemoteException re) {
                    String msg = "Can't unbind LoginService. ";
                    logger.severe(msg + LoggerUtils.getStackTrace(re));
                    throw new IllegalStateException(msg, re);
                }
            }

            if (admin != null) {
                logger.info("Shutting down Admin");
                admin.shutdown(force);
                admin = null;
            }

            active = false;
            this.notifyAll();
        }
    }

    @Override
    public boolean stopRequested() {
        return stopRequested;
    }

    /**
     *  Wait for the service to be started or stopped.
     */
    public synchronized void waitForActive(boolean desiredState)
        throws InterruptedException {

        while (active != desiredState) {
            this.wait();
        }
    }

    /**
     * Accessor for admin, which can be null.
     */
    public Admin getAdmin() {
        return admin;
    }

    /**
     * Accessor for params
     */
    public AdminServiceParams getParams() {
        return params;
    }

    /**
     * Accessor for security info
     */
    public AdminSecurity getAdminSecurity() {
        return adminSecurity;
    }

    /**
     * Accessor for login services
     */
    public LoginService getLoginService() {
        return loginService;
    }

    /**
     * Accessor for internal login manager
     */
    public InternalLoginManager getLoginManager() {
        return (adminSecurity == null) ? null : adminSecurity.getLoginManager();
    }

    /**
     * Configure the store name and then create the Admin instance, which
     * creates the Admin database.  This method can be used only when the
     * AdminService is running in bootstrap/configuration mode.
     */
    public void configure(String storeName) {
        assert admin == null;
        params.getGlobalParams().setKVStoreName(storeName);

        /*
         * Since we are bootstrapping, there is a chicken-egg problem regarding
         * the HA service port.  The bootstrap parameters have an HA port range
         * configured for the SNA.  We know that none of these are in use at
         * this time, so we will commandeer the first port in this range for
         * now.  Later, when this #1 admin is officially deployed via the
         * deployment plan, we will note the use of this port in the Admin's
         * parameter record, thereby reserving its use with the PortTracker.
         */
        final StorageNodeParams snp = params.getStorageNodeParams();

        final int haPort = PortRange.getRange(snp.getHAPortRange()).get(0);
        AdminParams ap = params.getAdminParams();
        ap.setJEInfo(snp.getHAHostname(), haPort, snp.getHAHostname(), haPort);

        admin = new Admin(params, this);

        /* Now we can use the real log configuration. */
        logger.info("Changing log files to log directory for store " +
                    storeName);
        logger = LoggerUtils.getLogger(this.getClass(), params);
        faultHandler.setLogger(logger);
        adminSecurity.configure(storeName);
        if (loginService != null) {
            loginService.resetLogger(logger);
        }
        logger.info("Configured Admin for store: " + storeName);

        /* We can install the login updater now */
        final SecurityParams securityParams = params.getSecurityParams();
        if (securityParams != null) {
            admin.installSecurityUpdater();
        }

        startWebService();
    }

    /**
     * Subordinate services (e.g. CommandService) use this
     * method when logging.  AdminService's logger can change during
     * bootstrap.
     */
    public Logger getLogger() {
        return logger;
    }

    /**
     * Returns the fault handler associated with the service
     */
    public ProcessFaultHandler getFaultHandler() {
       return faultHandler;
    }

    @Override
    public boolean getUsingThreads() {
        return usingThreads;
    }

    /**
     * Initialize our AdminServiceParams member based on the contents of a
     * BootParams instance.
     */
    private void deriveASParams(BootstrapParams bp, SecurityParams sp) {

        String storeName = bp.getStoreName();
        StorageNodeId snid =
            new StorageNodeId(storeName == null ? 1 : bp.getId());

        GlobalParams gp = new GlobalParams(storeName);

        /*
         * Pass user-defined external authentication method to global parameter
         * so that bootstrap admin can recognize this setting automatically.
         */
        final String externalAuths = bp.getUserExternalAuth();
        gp.setUserExternalAuthMethods(externalAuths);

        /*
         * In this release, once the IDCS OAuth authentication mechanism is
         * enabled, the whole store won't allow session extension.
         *
         * TODO: Remove this restriction by distinguishing sessions created
         * after successfully login using different authentication methods.
         */
        if (SecurityUtils.hasIDCSOAuth(externalAuths)) {
            gp.setSessionExtendAllow("false");
        }

        StorageNodeParams snp =
            new StorageNodeParams(snid, bp.getHostname(), bp.getRegistryPort(),
                                  "Admin Bootstrap");

        snp.setRootDirPath(bp.getRootdir());
        /*
         * Setting Admin Directory property for configure store command
         * support
         */
        snp.setAdminDirMap(bp.getAdminDirMap());
        snp.setHAHostname(bp.getHAHostname());
        snp.setHAPortRange(bp.getHAPortRange());
        snp.setServicePortRange(bp.getServicePortRange());
        snp.setAdminWebPort(bp.getAdminWebServicePort());

        final AdminParams ap = new AdminParams(new AdminId(1),
                                               snp.getStorageNodeId(),
                                               AdminType.PRIMARY);
        params = new AdminServiceParams(sp, gp, snp, ap);
    }

    /**
     * Issue a BDBJE update of the target node's HA address.
     */
    public void updateMemberHAAddress(AdminId targetId,
                                      String targetHelperHosts,
                                      String newNodeHostPort) {

        /*
         * Setup the helper hosts to use for finding the master to execute this
         * update.
         */
        Set helperSockets = new HashSet();
        StringTokenizer tokenizer =
                new StringTokenizer(targetHelperHosts,
                                    ParameterUtils.HELPER_HOST_SEPARATOR);
        while (tokenizer.hasMoreTokens()) {
            String helper = tokenizer.nextToken();
            helperSockets.add(HostPortPair.getSocket(helper));
        }

        String storeName = params.getGlobalParams().getKVStoreName();
        String groupName = Admin.getAdminRepGroupName(storeName);

        /* Change the target node's HA address. */
        logger.info("Updating rep group " + groupName + " using helpers " +
                    targetHelperHosts + " to change " + targetId + " to " +
                    newNodeHostPort);

        String targetNodeName = Admin.getAdminRepNodeName(targetId);

        /*
         * Figure out the right ReplicationNetworkConfig to use.  If there's
         * an admin present, we just use that config.  Otherwise (not sure
         * why it wouldn't be if we are part of a replicated config),
         * construct a DataChannelFactory from the SecurityParams, if
         * present.
         */
        final ReplicationNetworkConfig repNetConfig;
        if (admin != null) {
            repNetConfig = admin.getRepNetConfig();
        } else if (params.getSecurityParams() == null) {
            repNetConfig = null;
        } else {
            final Properties haProps =
                params.getSecurityParams().getJEHAProperties();
            logger.info("DataChannelFactory: " +
                        haProps.getProperty(
                            ReplicationNetworkConfig.CHANNEL_TYPE));
            repNetConfig = ReplicationNetworkConfig.create(haProps);
        }

        ReplicationGroupAdmin rga =
            new ReplicationGroupAdmin(groupName, helperSockets, repNetConfig);

        ReplicationGroup rg = rga.getGroup();
        com.sleepycat.je.rep.ReplicationNode jeRN =
            rg.getMember(targetNodeName);
        if (jeRN == null) {
            throw new IllegalStateException
                (targetNodeName + " does not exist in replication group " +
                 groupName);
        }

        String newHostName =  HostPortPair.getHostname(newNodeHostPort);
        int newPort =  HostPortPair.getPort(newNodeHostPort);

        if ((jeRN.getHostName().equals(newHostName)) &&
            (jeRN.getPort() == newPort)) {

            /*
             * This node is already changed, nothing more to do. Do this
             * check in case the change has been made previously, and this
             * node is alive, as the updateAddress() call will incur an
             * exception if the node is alive.
             */
            return;
        }

        rga.updateAddress(targetNodeName, newHostName, newPort);
    }

    public void installStatusReceiver(AdminStatusReceiver asr) {

        statusReceiver = asr;

        if (admin == null) {
            /* We're unconfigured; report waiting for deployment. */
            update(ServiceStatus.WAITING_FOR_DEPLOY);
        } else {
            /* Otherwise, if we're up and servicing calls, we are running. */
            update(ServiceStatus.RUNNING);
        }
    }

    @Override
    public void update(ServiceStatus newStatus) {
       updateAdminStatus(admin, newStatus);
    }

    void updateAdminStatus(Admin a, ServiceStatus newStatus) {
        if (statusReceiver == null) {
            return;
        }

        /*
         * During bootstrapping, we have no Admin, so we can't reliably add the
         * ParameterListener when installing the receiver.  This method is
         * called at receiver installation time, and also when the Admin's
         * status changes.  When it changes from bootstrap to configured mode,
         * we can install the listener here.  Also, the Admin is given as an
         * argument to this method because, during bootstrapping, this method
         * is called before AdminService's admin instance variable is assigned.
         */
        if (a != null && parameterListener == null) {
            parameterListener = new ParameterChangeListener();
            a.addParameterListener(parameterListener);
            /* Prime the pump with the first newParameters call. */
            parameterListener.newParameters
                (null,
                 a.getParams().getAdminParams().getMap());
        }

        /*
         * Use the same pattern as ParameterListener to add a plan state and
         * plan progress listener at the point when the Admin changes from
         * bootstrap to configured mode.
         */
        if (planStateListener == null) {
            planStateListener = new MgmtAgentPlanStateChangeTracker();
        }
        if (planProgressListener == null) {
            planProgressListener = new MgmtAgentPlanProgressTracker();
        }
        if (a != null) {
            /* Monitor listener set can filter out duplicated listener */
            a.getMonitor().trackPlanStateChange(planStateListener);
            a.getMonitor().trackPlanProgress(planProgressListener);
        }

        State adminState = (a == null ? null : a.getReplicationMode());

        boolean isMaster =
            (adminState == null || adminState != State.MASTER ? false : true);

        try {
            statusReceiver.updateAdminStatus
                (new ServiceStatusChange(newStatus), isMaster);
        } catch (RemoteException e) {
            /*
             * This should not prevent the admin from coming up.  Just log the
             * failure.
             */
            logger.log
                (Level.WARNING,
                 "Failed to send status updateof " + newStatus.toString() +
                 " to MgmtAgent", e);
        }
    }

    public RoleResolver getRoleResolver() {
        return (adminSecurity == null) ? null : adminSecurity.getRoleResolver();
    }

    private void startWebService() {

        if (webServer != null) {
            /*
             * When the first admin turned from bootstrap to normal admin.
             * The logger location will change, reset the logger handle
             * inside SkLogger so that all loggers can be refreshed.
             */
            webServer.getLogger().resetLogger(logger);
            return;
        }

        final StorageNodeParams snp = params.getStorageNodeParams();
        int httpPort = snp.getAdminWebPort();

        if (httpPort <= 0) {
            logger.info("Admin web service is not starting up, web port: " +
                        httpPort);
            return;
        }

        final SecurityParams secParams = params.getSecurityParams();

        SslContext ctx = null;
        if (secParams != null && secParams.isSecure() &&
            !secParams.allTransportSSLDisabled()) {
            try {
                KeyManagerFactory kmf = secParams.createSSLKeyManagerFactory();
                SslContextBuilder builder = SslContextBuilder.forServer(kmf);
                ctx = builder.build();
            } catch (Exception e) {
                logger.severe("Fail to build SSL context: " + e);
                throw new IllegalStateException(e);
            }
        }

        int httpsPort = 0;
        if (ctx != null) {
            httpsPort = httpPort;
            httpPort = 0;
        }

        try {

            final SkLogger skLogger = new SkLogger(logger);
            final LogControl logControl = new LogControl();
            ProxyRequestHandler handler =
                new ProxyRequestHandler(logControl, skLogger);
            adminWebService = new AdminWebService(exportableCommandService,
                                                  loginService,
                                                  skLogger).initService();
            handler.addService(ADMIN_PATH_NAME, adminWebService);

            webServer = new HttpServer(snp.getHostname(),
                                       httpPort, /* httpPort */
                                       httpsPort, /* httpsPort */
                                       0, /* numAcceptThreads */
                                       0, /* numWorkerThreads */
                                       0, /* maxRequestSize */
                                       0, /* maxChunkSize */
                                       0, /* idleReadTimeout */
                                       handler,
                                       ctx,
                                       skLogger);
            logger.info("Started web service");
        } catch (Exception e) {
            logger.severe("Fail to start up HTTP server: " + e);
            throw new IllegalStateException(e);
        }
    }

    /**
     * The plan state change tracker for updating the status receiver.
     */
    private class MgmtAgentPlanStateChangeTracker
        implements ViewListener {

        @Override
        public void newInfo(ResourceId resourceId, PlanStateChange planState) {
            try {
                statusReceiver.updatePlanStatus(planState.toJsonString());
            } catch (RemoteException re) {
                /* If we fail to deliver, who can we tell about it? */
                logger.log
                    (Level.WARNING,
                     "Failure to deliver plan state change to MgmtAgent", re);
                return;
            }
        }
    }

    /**
     * The plan progress tracker for updating the status receiver.
     */
    private class MgmtAgentPlanProgressTracker
        implements ViewListener {

        @Override
        public void newInfo(ResourceId resourceId, PlanProgress planProgress) {
            try {
                statusReceiver.updatePlanStatus(planProgress.toJsonString());
            } catch (RemoteException re) {
                /* If we fail to deliver, who can we tell about it? */
                logger.log
                    (Level.WARNING,
                     "Failure to deliver plan progress to MgmtAgent", re);
                return;
            }
        }
    }

    /**
     * The parameter listener for updating the status receiver.
     */
    private class ParameterChangeListener implements ParameterListener {

        @Override
        public void newParameters(ParameterMap oldMap, ParameterMap newMap) {
            try {
                statusReceiver.receiveNewParams(newMap);
            } catch (RemoteException re) {
                /* If we fail to deliver, who can we tell about it? */
                logger.log
                    (Level.WARNING,
                     "Failure to deliver parameter change to MgmtAgent", re);
                return;
            }
        }
    }

    /**
     * Helper class to create and then export a secured RMI service.
     */
    private abstract class ServiceBinder {
        private final String svcName;
        protected final AdminService aservice;
        protected I insecureService;
        private S secureService;

        /**
         * Instantiating a ServiceBinder will actually invoke the creation and
         * proxy wrapping of the targer service.
         *
         * @param svcName is the RMI service name.
         */
        ServiceBinder(String svcName, AdminService aservice) {
            this.svcName = svcName;
            this.aservice = aservice;

            /* create and wrap the RMI service */
            initService();
        }

        public S getSecureService() {
            return secureService;
        }

        public I getInsecureService() {
            return insecureService;
        }

        /** Instantiate an insecure version of the service */
        abstract I makeInsecureService();

        /** Return the Remote from the insecure version of the service. */
        abstract S getRemote();

        /**
         * Start the service, and wrap it with the appropriate security proxy
         */
        private void initService() {
            final StorageNodeParams snParams = params.getStorageNodeParams();
            final String hostName = snParams.getHostname();
            final int registryPort = snParams.getRegistryPort();
            final String kvStoreName =
                params.getGlobalParams().getKVStoreName();

            logger.info("Starting " + svcName + " on rmi://" + hostName + ":" +
                        registryPort + "/" + svcName);
            try {
                insecureService = makeInsecureService();

                /* Wrap the insecure service in a secure proxy */
                try {
                    secureService = SecureProxy.create
                        (getRemote(),
                         adminSecurity.getAccessChecker(),
                         faultHandler);
                    logger.info("Successfully created a secure proxy for " +
                                svcName);
                } catch (ConfigurationException ce) {
                    throw new IllegalStateException
                        ("Unable to create a secure proxy for " + svcName, ce);
                }

                RMISocketPolicy rmiSocketPolicy =
                    params.getSecurityParams().getRMISocketPolicy();
                final SocketFactoryPair sfp =
                    params.getStorageNodeParams().getAdminCommandServiceSFP
                    (rmiSocketPolicy, kvStoreName);

                if (sfp.getServerFactory() != null) {
                    sfp.getServerFactory().setConnectionLogger(logger);
                }

                RegistryUtils.rebind(hostName,
                                     registryPort,
                                     svcName,
                                     secureService,
                                     sfp.getClientFactory(),
                                     sfp.getServerFactory());

            } catch (RemoteException re) {
                String msg = "Starting " + svcName + " failed";
                logger.severe(msg + LoggerUtils.getStackTrace(re));
                throw new IllegalStateException(msg, re);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy