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

Java tutorial

Introduction

Here is the source code for io.smartspaces.spacecontroller.runtime.StandardSpaceControllerDataBundleManager.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.SmartSpacesException;
import io.smartspaces.common.ResourceRepositoryUploadChannel;
import io.smartspaces.liveactivity.runtime.LiveActivityStorageManager;
import io.smartspaces.liveactivity.runtime.domain.InstalledLiveActivity;
import io.smartspaces.util.io.FileSupport;
import io.smartspaces.util.io.FileSupportImpl;
import io.smartspaces.util.web.HttpClientHttpContentCopier;
import io.smartspaces.util.web.HttpContentCopier;

import org.apache.commons.logging.Log;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipOutputStream;

/**
 * Controller-side manager for copying data bundles between master and
 * controller.
 *
 * @author Trevor Pering
 */
public class StandardSpaceControllerDataBundleManager implements SpaceControllerDataBundleManager {

    /**
     * Constant identifying the data bundle entry for the controller data.
     */
    public static final String CONTROLLER_DATA_BUNDLE_ENTRY = "controller";

    /**
     * Path prefix to use for activity data bundle sections.
     */
    public static final String ACTIVITY_DATA_BUNDLE_PREFIX = "activity/";

    /**
     * The content copier used for actually copying bundles.
     */
    private final HttpContentCopier contentCopier;

    /**
     * The FileZipper to use for managing zip files.
     */
    private final FileSupport fileSupport;

    /**
     * The space controller managed by this manager.
     */
    private SpaceControllerControl spaceController;

    /**
     * The activity storage manager.
     */
    private LiveActivityStorageManager activityStorageManager;

    /**
     * Create an instance using a default content copier.
     */
    public StandardSpaceControllerDataBundleManager() {
        this(new HttpClientHttpContentCopier(), FileSupportImpl.INSTANCE);
    }

    /**
     * Create an instance using the supplied content copier and file zipper.
     *
     * @param contentCopier
     *          the content copier to use fot his manager
     * @param fileSupport
     *          interface for file support utilities
     */
    public StandardSpaceControllerDataBundleManager(HttpContentCopier contentCopier, FileSupport fileSupport) {
        this.contentCopier = contentCopier;
        this.fileSupport = fileSupport;
    }

    @Override
    public void startup() {
        contentCopier.startup();
    }

    @Override
    public void shutdown() {
        contentCopier.shutdown();
    }

    @Override
    public void captureControllerDataBundle(String destinationUri) {
        File dataBundle = createDataBundle();
        try {
            Map<String, String> parameters = new HashMap<>();
            parameters.put("uuid", getControllerUuid());
            String channel = ResourceRepositoryUploadChannel.DATA_BUNDLE_UPLOAD.getChannelId();
            contentCopier.copyTo(destinationUri, dataBundle, channel, parameters);
        } finally {
            dataBundle.delete();
        }
    }

    @Override
    public void restoreControllerDataBundle(String sourceUri) {
        File dataBundle = generateDataBundleTempFile();
        try {
            contentCopier.copy(sourceUri, dataBundle);
            extractFullDataBundle(dataBundle);
        } finally {
            dataBundle.delete();
        }
    }

    /**
     * Create a data bundle for this controller.
     *
     * @return the created data bundle file
     */
    private File createDataBundle() {
        File dataBundle = generateDataBundleTempFile();
        ZipOutputStream zipOutputStream = null;
        try {
            zipOutputStream = fileSupport.createZipOutputStream(dataBundle);
            addDataBundleSection(zipOutputStream, CONTROLLER_DATA_BUNDLE_ENTRY,
                    getControllerDataContentDirectory());
            for (InstalledLiveActivity activity : spaceController.getAllInstalledLiveActivities()) {
                addDataBundleSection(zipOutputStream, ACTIVITY_DATA_BUNDLE_PREFIX + activity.getUuid(),
                        getActivityDataContentDirectory(activity));
            }
            zipOutputStream.close();
            zipOutputStream = null;
        } catch (Exception e) {
            dataBundle.delete();

            throw new SmartSpacesException(
                    String.format("Error while zipping data bundle %s", dataBundle.getAbsolutePath()), e);
        } finally {
            fileSupport.close(zipOutputStream, true);
        }

        return dataBundle;
    }

    /**
     * Add a data bundle section to the supplied output stream.
     *
     * @param zipOutputStream
     *          stream to which to add the component data bundle
     * @param subsection
     *          subsection in the zip stream to add it to
     * @param contentDirectory
     *          the data bundle directory to add
     */
    private void addDataBundleSection(ZipOutputStream zipOutputStream, String subsection, File contentDirectory) {
        if (contentDirectory.exists()) {
            getLog().info(String.format("Adding data bundle content source %s from directory %s", subsection,
                    contentDirectory.getAbsolutePath()));
            File relFile = new File(".");
            fileSupport.addFileToZipStream(zipOutputStream, contentDirectory, relFile, subsection);
        } else {
            getLog().warn(
                    String.format("Skipping non-existent data directory %s", contentDirectory.getAbsolutePath()));
        }
    }

    /**
     * Extract the given data bundle file for this controller, including both
     * controller and activity portions.
     *
     * @param dataBundle
     *          data bundle file to extract
     */
    private void extractFullDataBundle(File dataBundle) {
        File incomingDirectory = generateDataBundleTempFile();

        try {
            fileSupport.unzip(dataBundle, incomingDirectory);

            extractDataBundleSection(incomingDirectory, CONTROLLER_DATA_BUNDLE_ENTRY,
                    getControllerDataContentDirectory());

            for (InstalledLiveActivity activity : spaceController.getAllInstalledLiveActivities()) {
                extractDataBundleSection(incomingDirectory, ACTIVITY_DATA_BUNDLE_PREFIX + activity.getUuid(),
                        getActivityDataContentDirectory(activity));
            }

        } catch (Exception e) {
            throw new SmartSpacesException("Extracting data bundle " + dataBundle.getName(), e);
        } finally {
            fileSupport.delete(incomingDirectory);
        }
    }

    /**
     * Extract a data bundle section from the compete bundle.
     *
     * @param incomingDirectory
     *          directory containing the extracted master data bundle
     * @param subsection
     *          subsection to extract
     * @param targetDirectory
     *          target location for the extracted bundle
     */
    private void extractDataBundleSection(File incomingDirectory, String subsection, File targetDirectory) {
        File sourceDirectory = new File(incomingDirectory, subsection);
        File backupDirectory = generateDataBundleTempFile();

        try {
            if (targetDirectory.exists() && !fileSupport.rename(targetDirectory, backupDirectory)) {
                throw new SimpleSmartSpacesException("Could not move existing directory to backup");
            }
            if (!sourceDirectory.exists()) {
                getLog().warn(String.format("No source directory found for %s, skipping", sourceDirectory));
            } else {
                getLog().info(String.format("Extracting source %s to %s, backup %s", sourceDirectory,
                        targetDirectory, backupDirectory));
                if (!fileSupport.rename(sourceDirectory, targetDirectory)) {
                    throw new SimpleSmartSpacesException("Could not move incoming directory to target");
                }
            }
            fileSupport.delete(backupDirectory);
        } catch (Exception e) {
            if (backupDirectory.exists() && !targetDirectory.exists()) {
                backupDirectory.renameTo(targetDirectory);
            }
            fileSupport.delete(sourceDirectory);
            throw new SmartSpacesException("Renaming content directories for " + targetDirectory.getAbsolutePath(),
                    e);
        }
    }

    @Override
    public void setSpaceController(SpaceControllerControl spaceController) {
        this.spaceController = spaceController;
    }

    @Override
    public void setActivityStorageManager(LiveActivityStorageManager activityStorageManager) {
        this.activityStorageManager = activityStorageManager;
    }

    /**
     * Get the assigned controller UUID.
     *
     * @return uuid of the associated controller
     */
    private String getControllerUuid() {
        return spaceController.getControllerInfo().getUuid();
    }

    /**
     * Get a temporary file location, guaranteed not to exist.
     *
     * @return temp file for this bundle
     */
    private File generateDataBundleTempFile() {
        File tempRoot = spaceController.getSpaceEnvironment().getFilesystem().getTempDirectory("dataBundle");
        fileSupport.directoryExists(tempRoot);
        File tempFile;
        do {
            String filename = String.format("temp-%x", Math.round(Math.random() * Integer.MAX_VALUE));
            tempFile = new File(tempRoot, filename);
        } while (tempFile.exists());
        return tempFile;
    }

    /**
     * Get the content directory for this controller.
     *
     * @return the content directory for the controller
     */
    private File getControllerDataContentDirectory() {
        return spaceController.getSpaceEnvironment().getFilesystem().getDataDirectory();
    }

    /**
     * Get the content directory for an activity.
     *
     * @param activity
     *          activity to query
     *
     * @return the content directory for the activity
     */
    private File getActivityDataContentDirectory(InstalledLiveActivity activity) {
        return activityStorageManager.getActivityFilesystem(activity.getUuid()).getPermanentDataDirectory();
    }

    /**
     * @return logger to use
     */
    private Log getLog() {
        return spaceController.getSpaceEnvironment().getLog();
    }
}