com.emc.ecs.sync.target.CuaFilesystemTarget.java Source code

Java tutorial

Introduction

Here is the source code for com.emc.ecs.sync.target.CuaFilesystemTarget.java

Source

/*
 * Copyright 2013-2015 EMC Corporation. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * or in the "license" file accompanying this file. This file 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 com.emc.ecs.sync.target;

import com.emc.ecs.sync.filter.SyncFilter;
import com.emc.ecs.sync.model.object.ClipSyncObject;
import com.emc.ecs.sync.model.object.SyncObject;
import com.emc.ecs.sync.source.SyncSource;
import com.emc.ecs.sync.util.CasUtil;
import com.emc.ecs.sync.util.ClipTag;
import com.filepool.fplibrary.FPLibraryException;
import com.filepool.fplibrary.FPTag;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.Date;
import java.util.Iterator;

/**
 * Plugin to extract files from a CUA (written to CAS) and replicate the source path structure on a local filesystem.
 */
public class CuaFilesystemTarget extends SyncTarget {
    private static final Logger log = LoggerFactory.getLogger(CuaFilesystemTarget.class);

    private static final String CLIP_NAME = "Storigen_File_Gateway";
    private static final String ATTRIBUTE_TAG_NAME = "Storigen_File_Gateway_File_Attributes";
    private static final String BLOB_TAG_NAME = "Storigen_File_Gateway_Blob";

    private static final String PATH_ATTRIBUTE = "Storigen_File_Gateway_File_Path0";
    private static final String ITIME_ATTRIBUTE = "Storigen_File_Gateway_File_CTime"; // Windows ctime means create time
    private static final String MTIME_ATTRIBUTE = "Storigen_File_Gateway_File_MTime";
    private static final String ATIME_ATTRIBUTE = "Storigen_File_Gateway_File_ATime";
    private static final String SIZE_HI_ATTRIBUTE = "Storigen_File_Gateway_File_Size_Hi";
    private static final String SIZE_LO_ATTRIBUTE = "Storigen_File_Gateway_File_Size_Lo";

    private String target;
    private File targetDir;

    @Override
    public boolean canHandleTarget(String targetUri) {
        return false; // this plug-in is not CLI capable
    }

    @Override
    public Options getCustomOptions() {
        return new Options();
    }

    @Override
    protected void parseCustomOptions(CommandLine line) {
    }

    @Override
    public void configure(SyncSource source, Iterator<SyncFilter> filters, SyncTarget target) {
        targetDir = new File(this.target);
        if (!targetDir.exists())
            throw new IllegalArgumentException("target directory must exist");
        if (!targetDir.isDirectory())
            throw new IllegalArgumentException("target must be a directory");
        if (!targetDir.canWrite())
            throw new IllegalArgumentException("target must be writable");
    }

    @Override
    public void filter(SyncObject obj) {
        timeOperationStart(CasUtil.OPERATION_TOTAL);

        if (!(obj instanceof ClipSyncObject))
            throw new UnsupportedOperationException("sync object was not a CAS clip");
        final ClipSyncObject clipSync = (ClipSyncObject) obj;

        try {
            // looking for clips with a specific name
            if (!clipSync.getClipName().equals(CLIP_NAME)) {
                log.debug("skipped clip {} (clip name did not match)", clipSync.getRawSourceIdentifier());
            } else {

                ClipTag blobTag = null;
                String filePath = null;
                Date itime = null, mtime = null, atime = null;
                long hi = 0, lo = 0;
                for (ClipTag clipTag : clipSync.getTags()) {
                    FPTag sourceTag = clipTag.getTag();
                    if (sourceTag.getTagName().equals(ATTRIBUTE_TAG_NAME)) {
                        // pull all pertinent attributes
                        filePath = sourceTag.getStringAttribute(PATH_ATTRIBUTE);
                        itime = new Date(sourceTag.getLongAttribute(ITIME_ATTRIBUTE) * 1000); // tag value is in seconds
                        mtime = new Date(sourceTag.getLongAttribute(MTIME_ATTRIBUTE) * 1000); // .. convert to ms
                        atime = new Date(sourceTag.getLongAttribute(ATIME_ATTRIBUTE) * 1000);
                        hi = sourceTag.getLongAttribute(SIZE_HI_ATTRIBUTE);
                        lo = sourceTag.getLongAttribute(SIZE_LO_ATTRIBUTE);
                    } else if (sourceTag.getTagName().equals(BLOB_TAG_NAME)) {
                        blobTag = clipTag;
                    }
                }

                // sanity check
                if (blobTag == null)
                    throw new RuntimeException("could not find blob tag");
                if (filePath == null)
                    throw new RuntimeException("could not find file path attribute");
                // assume the rest have been pulled

                // make file path relative
                if (filePath.startsWith("/"))
                    filePath = filePath.substring(1);

                File destFile = new File(targetDir, filePath);
                obj.setTargetIdentifier(destFile.getPath());

                // transfer the clip if:
                // - force is enabled
                // - target does not exist
                // - source mtime is after target mtime, or
                // - source size is different from target size
                long size = (lo > 0 ? hi << 32 : hi) + lo;
                if (force || !destFile.exists() || mtime.after(new Date(destFile.lastModified()))
                        || size != destFile.length()) {
                    log.info("writing {} to {}", obj.getSourceIdentifier(), destFile);

                    // make parent directories
                    mkdirs(destFile.getParentFile());

                    // write the file
                    try (OutputStream out = new FileOutputStream(destFile)) {
                        blobTag.writeToStream(out);
                    }

                    // set times
                    log.debug("updating timestamps for {}", destFile.getPath());
                    Path destPath = destFile.toPath();
                    Files.setAttribute(destPath, "creationTime", FileTime.fromMillis(itime.getTime()));
                    Files.setAttribute(destPath, "lastModifiedTime", FileTime.fromMillis(mtime.getTime()));
                    Files.setAttribute(destPath, "lastAccessTime", FileTime.fromMillis(atime.getTime()));
                    if (!destFile.setLastModified(mtime.getTime()))
                        log.warn("could not set mtime for {}", destFile.getPath());

                    // verify size
                    if (size != destFile.length())
                        throw new RuntimeException(String
                                .format("target file %s is not the right size (expected %d)", destFile, size));

                } else {
                    log.info("{} is up to date, skipping", destFile);
                }
            }

            timeOperationComplete(CasUtil.OPERATION_TOTAL);
        } catch (Throwable t) {
            timeOperationFailed(CasUtil.OPERATION_TOTAL);
            if (t instanceof RuntimeException)
                throw (RuntimeException) t;
            if (t instanceof FPLibraryException)
                throw new RuntimeException(
                        "Failed to store object: " + CasUtil.summarizeError((FPLibraryException) t), t);
            throw new RuntimeException("failed to sync object: " + t.getMessage(), t);
        }
    }

    @Override
    public SyncObject reverseFilter(SyncObject obj) {
        throw new UnsupportedOperationException(
                getClass().getSimpleName() + " does not yet support reverse filters (verification)");
    }

    /**
     * Synchronized mkdir to prevent conflicts in threaded environment.
     */
    private static synchronized void mkdirs(File dir) {
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                throw new RuntimeException("failed to create directory " + dir);
            }
        }
    }

    @Override
    public String getName() {
        return "CuaFilesystem Target";
    }

    @Override
    public String getDocumentation() {
        return "Custom target for CUA clips written to CAS to be exported as files.";
    }

    public String getTarget() {
        return target;
    }

    public void setTarget(String target) {
        this.target = target;
    }
}