org.duracloud.snapshottask.snapshot.CreateSnapshotTaskRunner.java Source code

Java tutorial

Introduction

Here is the source code for org.duracloud.snapshottask.snapshot.CreateSnapshotTaskRunner.java

Source

/*
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 *     http://duracloud.org/license/
 */
package org.duracloud.snapshottask.snapshot;

import java.io.IOException;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.http.HttpHeaders;
import org.duracloud.common.constant.Constants;
import org.duracloud.common.util.DateUtil;
import org.duracloud.common.web.RestHttpHelper;
import org.duracloud.snapshot.SnapshotConstants;
import org.duracloud.snapshot.dto.bridge.CreateSnapshotBridgeParameters;
import org.duracloud.snapshot.dto.bridge.CreateSnapshotBridgeResult;
import org.duracloud.snapshot.dto.task.CreateSnapshotTaskParameters;
import org.duracloud.snapshot.id.SnapshotIdentifier;
import org.duracloud.snapshotstorage.SnapshotStorageProvider;
import org.duracloud.storage.error.ServerConflictException;
import org.duracloud.storage.error.StorageStateException;
import org.duracloud.storage.error.TaskException;
import org.duracloud.storage.provider.StorageProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Begins the process of creating a snapshot by collecting the necessary
 * information and passing it down to the snapshot bridge application. Along
 * the way, the space provided is also set to read-only so that changes cannot
 * be made to the content.
 *
 * @author: Bill Branan
 * Date: 2/1/13
 */
public class CreateSnapshotTaskRunner extends SpaceModifyingSnapshotTaskRunner {

    private Logger log = LoggerFactory.getLogger(CreateSnapshotTaskRunner.class);

    private String dcAccountName;
    private String dcHost;
    private String dcPort;
    private String dcStoreId;
    private String bridgeMemberId;

    public CreateSnapshotTaskRunner(StorageProvider snapshotProvider,
            SnapshotStorageProvider unwrappedSnapshotProvider, String dcHost, String dcPort, String dcStoreId,
            String dcAccountName, String dcSnapshotUser, String bridgeAppHost, String bridgeAppPort,
            String bridgeAppUser, String bridgeAppPass, String bridgeMemberId) {
        super(snapshotProvider, unwrappedSnapshotProvider, dcSnapshotUser, bridgeAppHost, bridgeAppPort,
                bridgeAppUser, bridgeAppPass);
        this.dcHost = dcHost;
        this.dcPort = dcPort;
        this.dcStoreId = dcStoreId;
        this.dcAccountName = dcAccountName;
        this.bridgeMemberId = bridgeMemberId;
    }

    @Override
    public String getName() {
        return SnapshotConstants.CREATE_SNAPSHOT_TASK_NAME;
    }

    @Override
    public String performTask(String taskParameters) {
        log.info(
                "Performing SNAPSHOT task with parameters, "
                        + "DuraCloud Host: {} DuraCloud Port: {} DuraCloud StoreID: {} "
                        + "Account Name: {} DuraCloud Snapshot User: {} Bridge Host: {} "
                        + "Bridge Port: {} Bridge User: {} Member Id: {}",
                dcHost, dcPort, dcStoreId, dcAccountName, getSnapshotUser(), getBridgeAppHost(), getBridgeAppPort(),
                getBridgeAppUser(), bridgeMemberId);

        // Get input params
        CreateSnapshotTaskParameters taskParams = CreateSnapshotTaskParameters.deserialize(taskParameters);
        String spaceId = taskParams.getSpaceId();

        //check if snapshot  properties file already exists
        //and if so throw StorageStateException
        String snapshotId = getSnapshotIdFromProperties(spaceId);
        if (snapshotId != null) {
            throw new StorageStateException(MessageFormat.format(
                    "A snapshot ({0}) + is already underway for this space ({1})", snapshotId, spaceId), null);
        }

        // Generate snapshot ID
        long now = System.currentTimeMillis();
        snapshotId = generateSnapshotId(spaceId, now);

        // Pull together all snapshot properties
        Map<String, String> snapshotProps = new HashMap<>();
        snapshotProps.put("duracloud-host", dcHost);
        snapshotProps.put("duracloud-space-id", spaceId);
        snapshotProps.put("duracloud-store-id", dcStoreId);
        snapshotProps.put(Constants.SNAPSHOT_ID_PROP, snapshotId);
        snapshotProps.put("snapshot-date", DateUtil.convertToStringVerbose(now));
        snapshotProps.put("owner-id", dcAccountName);
        snapshotProps.put("description", taskParams.getDescription());
        snapshotProps.put("user-email", taskParams.getUserEmail());
        snapshotProps.put("member-id", bridgeMemberId);

        // Store snapshot properties in the snapshot space. This both provides
        // access to the properties down stream and effectively sets the space
        // to a read-only state.
        String serializedProps = buildSnapshotProps(snapshotProps);
        storeSnapshotProps(spaceId, serializedProps);

        // Add snapshot ID to space properties
        addSnapshotIdToSpaceProps(spaceId, snapshotId);

        // Give snapshot user read permissions on space
        setSnapshotUserPermissions(spaceId);

        // Create URL for call to bridge app
        String snapshotURL = buildSnapshotURL(snapshotId);

        String callResult;
        try {
            // Create body for call to bridge app
            String snapshotBody = buildSnapshotBody(taskParams);

            // Make call to the bridge ingest app to kick off transfer
            callResult = callBridge(createRestHelper(), snapshotURL, snapshotBody);
        } catch (Exception e) {
            // Bridge call did not complete successfully, clean up!
            try {
                removeSnapshotProps(spaceId);
                removeSnapshotIdFromSpaceProps(spaceId);
            } catch (Exception ex) {
                log.error("Failed to fully clean up snapshot props for " + spaceId + ": " + ex.getMessage(), ex);
            }

            if (!(e instanceof TaskException)) {
                throw new TaskException(e.getMessage());
            } else {
                throw (TaskException) e;
            }
        }

        CreateSnapshotBridgeResult bridgeResult = CreateSnapshotBridgeResult.deserialize(callResult);
        log.info("SNAPSHOT created with ID {} and status {}", bridgeResult.getSnapshotId(),
                bridgeResult.getStatus());

        return callResult;
    }

    /*
     * Generates a snapshot Id based on a set of variables
     */
    protected String generateSnapshotId(String spaceId, long timestamp) {
        SnapshotIdentifier snapshotIdentifier = new SnapshotIdentifier(dcAccountName, dcStoreId, spaceId,
                timestamp);
        return snapshotIdentifier.getSnapshotId();
    }

    /*
     * Create URL to call bridge app
     */
    protected String buildSnapshotURL(String snapshotId) {
        return MessageFormat.format("{0}/snapshot/{1}", buildBridgeBaseURL(), snapshotId);
    }

    /*
     * Creates the body of the request that will be sent to the bridge app
     */
    protected String buildSnapshotBody(CreateSnapshotTaskParameters taskParams) {
        CreateSnapshotBridgeParameters bridgeParams = new CreateSnapshotBridgeParameters(dcHost, dcPort, dcStoreId,
                taskParams.getSpaceId(), taskParams.getDescription(), taskParams.getUserEmail(), bridgeMemberId);
        return bridgeParams.serialize();
    }

    /**
     * Constructs the contents of a properties file given a set of
     * key/value pairs
     *
     * @param props snapshot properties
     * @return Properties-file formatted key/value pairs
     */
    protected String buildSnapshotProps(Map<String, String> props) {
        Properties snapshotProperties = new Properties();
        for (String key : props.keySet()) {
            snapshotProperties.setProperty(key, props.get(key));
        }

        StringWriter writer = new StringWriter();
        try {
            snapshotProperties.store(writer, null);
        } catch (IOException e) {
            throw new TaskException("Could not write snapshot properties: " + e.getMessage(), e);
        }
        writer.flush();
        return writer.toString();
    }

    /*
     * Calls the bridge application to create a snapshot
     */
    protected String callBridge(RestHttpHelper restHelper, String snapshotURL, String snapshotBody)
            throws Exception {
        log.info("Making SNAPSHOT call to URL {} with body {}", snapshotURL, snapshotBody);

        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaders.CONTENT_TYPE, "application/json");
        RestHttpHelper.HttpResponse response = restHelper.put(snapshotURL, snapshotBody, headers);
        int statusCode = response.getStatusCode();
        if (statusCode != 200 && statusCode != 201) {
            String responseStr = response.getResponseBody();

            try {
                String m = getMessageValue(responseStr);
                if (m != null) {
                    responseStr = m;
                }
            } catch (IOException ex) {
                log.warn(ex.getMessage(), ex);
            }

            if (statusCode == 409) {
                throw new ServerConflictException(responseStr);
            } else {
                throw new RuntimeException(responseStr + " (" + statusCode + ")");
            }

        }
        return response.getResponseBody();
    }
}