io.smartspaces.spacecontroller.runtime.StandardSpaceController.java Source code

Java tutorial

Introduction

Here is the source code for io.smartspaces.spacecontroller.runtime.StandardSpaceController.java

Source

/*
 * Copyright (C) 2016 Keith M. Hughes
 * Copyright (C) 2012 Google Inc.
 *
 * 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 io.smartspaces.spacecontroller.runtime;

import io.smartspaces.SimpleSmartSpacesException;
import io.smartspaces.activity.ActivityStatus;
import io.smartspaces.container.control.message.activity.LiveActivityDeleteRequest;
import io.smartspaces.container.control.message.activity.LiveActivityDeleteResult;
import io.smartspaces.container.control.message.activity.LiveActivityDeleteResult.LiveActivityDeleteStatus;
import io.smartspaces.container.control.message.activity.LiveActivityDeploymentRequest;
import io.smartspaces.container.control.message.activity.LiveActivityDeploymentResult;
import io.smartspaces.container.control.message.container.resource.deployment.ContainerResourceDeploymentCommitRequest;
import io.smartspaces.container.control.message.container.resource.deployment.ContainerResourceDeploymentCommitResponse;
import io.smartspaces.container.control.message.container.resource.deployment.ContainerResourceDeploymentQueryRequest;
import io.smartspaces.container.control.message.container.resource.deployment.ContainerResourceDeploymentQueryResponse;
import io.smartspaces.container.data.SpaceControllerInformationValidator;
import io.smartspaces.domain.basic.pojo.SimpleSpaceController;
import io.smartspaces.liveactivity.runtime.LiveActivityRunner;
import io.smartspaces.liveactivity.runtime.LiveActivityRuntime;
import io.smartspaces.liveactivity.runtime.LiveActivityStatusPublisher;
import io.smartspaces.liveactivity.runtime.domain.InstalledLiveActivity;
import io.smartspaces.spacecontroller.SpaceController;
import io.smartspaces.spacecontroller.SpaceControllerStatus;
import io.smartspaces.spacecontroller.resource.deployment.ContainerResourceDeploymentManager;
import io.smartspaces.spacecontroller.runtime.configuration.SpaceControllerConfigurationManager;
import io.smartspaces.system.SmartSpacesEnvironment;
import io.smartspaces.system.core.container.SmartSpacesSystemControl;
import io.smartspaces.tasks.SequentialTaskQueue;
import io.smartspaces.util.io.FileSupport;
import io.smartspaces.util.io.FileSupportImpl;
import io.smartspaces.util.uuid.JavaUuidGenerator;
import io.smartspaces.util.uuid.UuidGenerator;

import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.logging.Log;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * A base implementation of {@link SpaceController} which gives basic
 * implementation. Does not supply communication for the remote master.
 *
 * @author Keith M. Hughes
 */
public class StandardSpaceController extends BaseSpaceController
        implements SpaceControllerControl, LiveActivityStatusPublisher {

    /**
     * The amount of time to wait for shutting down the controller if some startup
     * error happens, in milliseconds.
     */
    public static final int CONTROLLER_ERROR_SHUTDOWN_WAIT_TIME = 5000;

    /**
     * An operation detail message when a live activity cannot be deleted because
     * it is still running.
     */
    public static final String OPERATION_DETAIL_DELETION_FAIL_LIVE_ACTIVITY_RUNNING = "The live activity is running";

    /**
     * The default number of milliseconds the controllerHeartbeat thread delays
     * between beats.
     */
    public static final int HEARTBEAT_DELAY_DEFAULT = 10000;

    /**
     * The heartbeatLoop for this controller.
     */
    private SpaceControllerHeartbeat controllerHeartbeat;

    /**
     * Control for the controllerHeartbeat.
     */
    private ScheduledFuture<?> controllerHeartbeatControl;

    /**
     * Number of milliseconds the heartbeatLoop waits before each beat.
     */
    private final long heartbeatDelay = HEARTBEAT_DELAY_DEFAULT;

    /**
     * Manager for space controller data operations.
     */
    private final SpaceControllerDataBundleManager dataBundleManager;

    /**
     * The runtime for live activities.
     */
    private LiveActivityRuntime liveActivityRuntime;

    /**
     * The Smart Spaces system controller.
     */
    private final SmartSpacesSystemControl spaceSystemControl;

    /**
     * The controller communicator for remote control.
     */
    private final SpaceControllerCommunicator spaceControllerCommunicator;

    /**
     * The sequential task queue to be used for controller tasks.
     */
    private SequentialTaskQueue eventQueue;

    /**
     * {@code true} if the controller was started up.
     */
    private volatile boolean startedUp = false;

    /**
     * The persister for information about the controller.
     */
    private final SpaceControllerInfoPersister controllerInfoPersister;

    /**
     * The configuration manager for the controller.
     */
    private final SpaceControllerConfigurationManager spaceControllerConfigurationManager;

    /**
     * File control for the controller.
     *
     * <p>
     * This can be {@code null} if it wasn't requested.
     */
    private SpaceControllerFileControl fileControl;

    /**
     * Activity installer for the controller.
     */
    private SpaceControllerActivityInstallationManager spaceControllerActivityInstallManager;

    /**
     * The container resource deployment manager.
     */
    private final ContainerResourceDeploymentManager containerResourceDeploymentManager;

    /**
     * Support for file operations.
     */
    private FileSupport fileSupport = FileSupportImpl.INSTANCE;

    /**
     * Construct a new StandardSpaceController.
     *
     * @param spaceControllerActivityInstaller
     *          activity installer
     * @param containerResourceDeploymentManager
     *          manager for deploying container resources
     * @param spaceControllerCommunicator
     *          the communicator for something controlling the controller
     * @param spaceControllerInfoPersister
     *          the persister for controller information
     * @param spaceSystemControl
     *          the system control for the container
     * @param dataBundleManager
     *          the manager for data bundle operations
     * @param spaceControllerConfigurationManager
     *          configuration manager for the space controller
     * @param liveActivityRuntime
     *          the live activity runtime for the controller
     * @param taskQueue
     *          the task queue for the controller
     * @param spaceEnvironment
     *          the space environment to use
     */
    public StandardSpaceController(SpaceControllerActivityInstallationManager spaceControllerActivityInstaller,
            ContainerResourceDeploymentManager containerResourceDeploymentManager,
            SpaceControllerCommunicator spaceControllerCommunicator,
            SpaceControllerInfoPersister spaceControllerInfoPersister, SmartSpacesSystemControl spaceSystemControl,
            SpaceControllerDataBundleManager dataBundleManager,
            SpaceControllerConfigurationManager spaceControllerConfigurationManager,
            LiveActivityRuntime liveActivityRuntime, SequentialTaskQueue taskQueue,
            SmartSpacesEnvironment spaceEnvironment) {
        super(spaceEnvironment);

        this.spaceControllerActivityInstallManager = spaceControllerActivityInstaller;
        this.containerResourceDeploymentManager = containerResourceDeploymentManager;
        this.spaceControllerConfigurationManager = spaceControllerConfigurationManager;

        this.dataBundleManager = dataBundleManager;
        dataBundleManager.setSpaceController(this);

        this.spaceControllerCommunicator = spaceControllerCommunicator;
        spaceControllerCommunicator.setSpaceControllerControl(this);

        this.controllerInfoPersister = spaceControllerInfoPersister;

        this.spaceSystemControl = spaceSystemControl;

        this.liveActivityRuntime = liveActivityRuntime;
        liveActivityRuntime.setLiveActivityStatusPublisher(this);

        this.eventQueue = taskQueue;
    }

    @Override
    public void startup() {
        super.startup();

        confirmUuid();

        final Log log = getSpaceEnvironment().getLog();

        spaceControllerCommunicator.onStartup(getControllerInfo());

        controllerHeartbeat = spaceControllerCommunicator.newSpaceControllerHeartbeat();
        controllerHeartbeatControl = getSpaceEnvironment().getExecutorService().scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    controllerHeartbeat.sendHeartbeat();
                } catch (Exception e) {
                    log.error("Exception while trying to send a Space Controller heartbeat", e);
                }
            }
        }, heartbeatDelay, heartbeatDelay, TimeUnit.MILLISECONDS);

        dataBundleManager.startup();

        startupControllerControl();

        spaceControllerCommunicator.registerControllerWithMaster(getControllerInfo());

        startupAutostartActivities();

        startedUp = true;
    }

    /**
     * Make sure the controller had a UUID. If not, generate one.
     */
    private void confirmUuid() {
        SimpleSpaceController spaceControllerInfo = getControllerInfo();

        SpaceControllerInformationValidator spaceControllerInformationValidator = new SpaceControllerInformationValidator();
        StringBuilder errorBuilder = spaceControllerInformationValidator
                .checkControllerInfoForErrors(spaceControllerInfo, getSpaceEnvironment().getLog());
        if (errorBuilder.length() != 0) {
            // DO NOT LIKE THIS BUT HOW TO SHUT THE CONTAINER DOWN WITHOUT TONS OF
            // STARTUP ERRORS.
            getSpaceEnvironment().getExecutorService().schedule(new Runnable() {
                @Override
                public void run() {
                    spaceSystemControl.shutdown();
                }
            }, CONTROLLER_ERROR_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS);

            throw new SimpleSmartSpacesException(errorBuilder.toString());
        }

        String uuid = spaceControllerInfo.getUuid();
        if (uuid == null || uuid.trim().isEmpty()) {
            UuidGenerator uuidGenerator = new JavaUuidGenerator();
            uuid = uuidGenerator.newUuid();
            spaceControllerInfo.setUuid(uuid);

            getSpaceEnvironment().getLog()
                    .warn(String.format("No controller UUID found, generated UUID is %s", uuid));

            controllerInfoPersister.persist(spaceControllerInfo, getSpaceEnvironment());
        }
    }

    /**
     * Save the controller information in the configurations.
     */

    @Override
    public void shutdown() {
        super.shutdown();
        if (startedUp) {
            try {
                liveActivityRuntime.shutdown();

                if (fileControl != null) {
                    fileControl.shutdown();
                    fileControl = null;
                }

                shutdownAllLiveActivities();

                controllerHeartbeatControl.cancel(true);
                controllerHeartbeatControl = null;

                dataBundleManager.shutdown();

                spaceControllerCommunicator.onShutdown();
            } finally {
                startedUp = false;
            }
        }
    }

    /**
     * Startup the activities that need to start up when the controller starts.
     */
    private void startupAutostartActivities() {
        for (InstalledLiveActivity activity : getAllInstalledLiveActivities()) {
            switch (activity.getRuntimeStartupType()) {
            case STARTUP:
                startupLiveActivity(activity.getUuid());
                break;
            case ACTIVATE:
                activateLiveActivity(activity.getUuid());
                break;
            case READY:
                break;
            default:
                getSpaceEnvironment().getLog().error(String.format("Unknown startup type %s for activity %s/%s",
                        activity.getRuntimeStartupType(), activity.getIdentifyingName(), activity.getUuid()));
            }
        }
    }

    @Override
    public void startupAllLiveActivities() {
        liveActivityRuntime.startupAllActivities();
    }

    @Override
    public void shutdownAllLiveActivities() {
        liveActivityRuntime.shutdownAllActivities();
    }

    @Override
    public void configureController(Map<String, String> configuration) {
        getSpaceEnvironment().getLog().info("Configuring the space controller");
        getSpaceEnvironment().getLog().info(configuration);

        spaceControllerConfigurationManager.update(configuration);
    }

    @Override
    public void captureControllerDataBundle(final String bundleUri) {
        getSpaceEnvironment().getLog().info("Capture controller data bundle");
        getSpaceEnvironment().getExecutorService().submit(new Runnable() {
            @Override
            public void run() {
                executeCaptureControllerDataBundle(bundleUri);
            }
        });
    }

    /**
     * Execute a capture controller data bundle request from the master. This will
     * trigger the data content push back to the master, and send off a
     * success/failure message.
     *
     * @param bundleUri
     *          Uri bundle to send the captured data bundle to.
     */
    private void executeCaptureControllerDataBundle(final String bundleUri) {
        try {
            dataBundleManager.captureControllerDataBundle(bundleUri);
            spaceControllerCommunicator.publishControllerDataStatus(SpaceControllerDataOperation.DATA_CAPTURE,
                    SpaceControllerStatus.SUCCESS, null);
        } catch (Exception e) {
            getSpaceEnvironment().getLog().error("Error capturing data bundle", e);
            spaceControllerCommunicator.publishControllerDataStatus(SpaceControllerDataOperation.DATA_CAPTURE,
                    SpaceControllerStatus.FAILURE, e);
        }
    }

    @Override
    public void restoreControllerDataBundle(final String bundleUri) {
        getSpaceEnvironment().getLog().info("Restore controller data bundle");
        getSpaceEnvironment().getExecutorService().submit(new Runnable() {
            @Override
            public void run() {
                executeRestoreControllerDataBundle(bundleUri);
            }
        });
    }

    /**
     * Execute the restore data bundle operation. Will trigger the restore, and
     * send back a success/failure message.
     *
     * @param bundleUri
     *          URI for the data bundle to be fetched from
     */
    private void executeRestoreControllerDataBundle(final String bundleUri) {
        try {
            dataBundleManager.restoreControllerDataBundle(bundleUri);
            spaceControllerCommunicator.publishControllerDataStatus(SpaceControllerDataOperation.DATA_RESTORE,
                    SpaceControllerStatus.SUCCESS, null);
        } catch (Exception e) {
            getSpaceEnvironment().getLog().error("Error restoring data bundle", e);
            spaceControllerCommunicator.publishControllerDataStatus(SpaceControllerDataOperation.DATA_RESTORE,
                    SpaceControllerStatus.FAILURE, e);
        }
    }

    @Override
    public void startupLiveActivity(String uuid) {
        liveActivityRuntime.startupLiveActivity(uuid);
    }

    @Override
    public void shutdownLiveActivity(final String uuid) {
        liveActivityRuntime.shutdownLiveActivity(uuid);
    }

    @Override
    public void statusLiveActivity(String uuid) {
        liveActivityRuntime.statusLiveActivity(uuid);
    }

    @Override
    public void activateLiveActivity(String uuid) {
        liveActivityRuntime.activateLiveActivity(uuid);
    }

    @Override
    public void deactivateLiveActivity(String uuid) {
        liveActivityRuntime.deactivateLiveActivity(uuid);
    }

    @Override
    public void configureLiveActivity(String uuid, Map<String, String> configuration) {
        liveActivityRuntime.configureLiveActivity(uuid, configuration);
    }

    @Override
    public void cleanLiveActivityTmpData(String uuid) {
        liveActivityRuntime.cleanLiveActivityTmpData(uuid);
    }

    @Override
    public void cleanLiveActivityPermanentData(String uuid) {
        liveActivityRuntime.cleanLiveActivityPermanentData(uuid);
    }

    @Override
    public void cleanControllerTempData() {
        getSpaceEnvironment().getLog().info("Cleaning controller temp directory");

        fileSupport.deleteDirectoryContents(getSpaceEnvironment().getFilesystem().getTempDirectory());
    }

    @Override
    public void cleanControllerPermanentData() {
        getSpaceEnvironment().getLog().info("Cleaning controller permanent directory");

        fileSupport.deleteDirectoryContents(getSpaceEnvironment().getFilesystem().getDataDirectory());
    }

    @Override
    public void cleanControllerTempDataAll() {
        getSpaceEnvironment().getLog().info("Cleaning all live activity temporary data directories");

        for (InstalledLiveActivity activity : getAllInstalledLiveActivities()) {
            cleanLiveActivityTmpData(activity.getUuid());
        }
    }

    @Override
    public void cleanControllerPermanentDataAll() {
        getSpaceEnvironment().getLog().info("Cleaning all live activity permanent data directories");

        for (InstalledLiveActivity activity : getAllInstalledLiveActivities()) {
            cleanLiveActivityPermanentData(activity.getUuid());
        }
    }

    @Override
    public LiveActivityRunner getLiveActivityRunnerByUuid(String uuid) {
        return liveActivityRuntime.getLiveActivityRunnerByUuid(uuid);
    }

    @Override
    public void publishActivityStatus(final String uuid, final ActivityStatus status) {
        eventQueue.addTask(new Runnable() {
            @Override
            public void run() {
                spaceControllerCommunicator.publishActivityStatus(uuid, status);
            }
        });
    }

    @Override
    public void shutdownControllerContainer() {
        spaceSystemControl.shutdown();
    }

    @Override
    public void hardRestartControllerContainer() {
        spaceSystemControl.hardRestart();
    }

    @Override
    public void softRestartControllerContainer() {
        spaceSystemControl.softRestart();
    }

    @Override
    public List<InstalledLiveActivity> getAllInstalledLiveActivities() {
        return liveActivityRuntime.getAllInstalledLiveActivities();
    }

    /**
     * Potentially start up any controller control points.
     */
    private void startupControllerControl() {
        boolean startupFileControl = getSpaceEnvironment().getSystemConfiguration()
                .getRequiredPropertyBoolean(SmartSpacesEnvironment.CONFIGURATION_NAME_CONTAINER_FILE_CONTROLLABLE);
        if (startupFileControl) {
            fileControl = new SpaceControllerFileControl(this, spaceSystemControl, getSpaceEnvironment());
            fileControl.startup();
        }
    }

    @Override
    public ContainerResourceDeploymentQueryResponse handleContainerResourceDeploymentQueryRequest(
            ContainerResourceDeploymentQueryRequest request) {
        return containerResourceDeploymentManager.queryResources(request);
    }

    @Override
    public ContainerResourceDeploymentCommitResponse handleContainerResourceDeploymentCommitRequest(
            ContainerResourceDeploymentCommitRequest request) {
        return containerResourceDeploymentManager.commitResources(request);
    }

    @Override
    public LiveActivityDeploymentResult installLiveActivity(LiveActivityDeploymentRequest request) {
        return spaceControllerActivityInstallManager.handleDeploymentRequest(request);
    }

    @Override
    public LiveActivityDeleteResult deleteLiveActivity(LiveActivityDeleteRequest request) {
        String uuid = request.getUuid();
        if (liveActivityRuntime.isLiveActivityRunning(uuid)) {
            getSpaceEnvironment().getLog().error(
                    String.format("Attempt to delete live activity %s failed, live activity is running", uuid));

            return new LiveActivityDeleteResult(uuid, LiveActivityDeleteStatus.FAILURE,
                    getSpaceEnvironment().getTimeProvider().getCurrentTime(),
                    OPERATION_DETAIL_DELETION_FAIL_LIVE_ACTIVITY_RUNNING);
        } else {
            return spaceControllerActivityInstallManager.handleDeleteRequest(request);
        }
    }

    /**
     * Set the file support to be used.
     *
     * <p>
     * This is for testing and no other reason.
     *
     * @param fileSupport
     *          the file support to use
     */
    @VisibleForTesting
    void setFileSupport(FileSupport fileSupport) {
        this.fileSupport = fileSupport;
    }
}