Package com.cloud.bridge.io

Source Code of com.cloud.bridge.io.S3FileSystemBucketAdapter

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 com.cloud.bridge.io;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.activation.DataHandler;
import javax.activation.DataSource;

import org.apache.log4j.Logger;

import com.cloud.bridge.service.core.s3.S3BucketAdapter;
import com.cloud.bridge.service.core.s3.S3MultipartPart;
import com.cloud.bridge.service.exception.FileNotExistException;
import com.cloud.bridge.service.exception.InternalErrorException;
import com.cloud.bridge.service.exception.OutOfStorageException;
import com.cloud.bridge.util.OrderedPair;
import com.cloud.bridge.util.StringHelper;

public class S3FileSystemBucketAdapter implements S3BucketAdapter {
    protected final static Logger logger = Logger.getLogger(S3FileSystemBucketAdapter.class);

    public S3FileSystemBucketAdapter() {
    }

    @Override
    public void createContainer(String mountedRoot, String bucket) {

        String dir = getBucketFolderDir(mountedRoot, bucket);
        File container = new File(dir);

        if (!container.exists()) {
            if (!container.mkdirs())
                throw new OutOfStorageException("Unable to create " + dir + " for bucket " + bucket);
        }
    }

    @Override
    public void deleteContainer(String mountedRoot, String bucket) {
        String dir = getBucketFolderDir(mountedRoot, bucket);
        File path = new File(dir);
        if (!deleteDirectory(path))
            throw new OutOfStorageException("Unable to delete " + dir + " for bucket " + bucket);
    }

    @Override
    public String getBucketFolderDir(String mountedRoot, String bucket) {
        String bucketFolder = getBucketFolderName(bucket);
        String dir;
        String separator = "" + File.separatorChar;
        if (!mountedRoot.endsWith(separator))
            dir = mountedRoot + separator + bucketFolder;
        else
            dir = mountedRoot + bucketFolder;

        return dir;
    }

    @Override
    public String saveObject(InputStream is, String mountedRoot, String bucket, String fileName) {
        FileOutputStream fos = null;
        MessageDigest md5 = null;

        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            logger.error("Unexpected exception " + e.getMessage(), e);
            throw new InternalErrorException("Unable to get MD5 MessageDigest", e);
        }

        File file = new File(getBucketFolderDir(mountedRoot, bucket) + File.separatorChar + fileName);
        try {
            // -> when versioning is off we need to rewrite the file contents
            file.delete();
            file.createNewFile();

            fos = new FileOutputStream(file);
            byte[] buffer = new byte[4096];
            int len = 0;
            while ((len = is.read(buffer)) > 0) {
                fos.write(buffer, 0, len);
                md5.update(buffer, 0, len);

            }
            //Convert MD4 digest to (lowercase) hex String
            return StringHelper.toHexString(md5.digest());

        } catch (IOException e) {
            logger.error("Unexpected exception " + e.getMessage(), e);
            throw new OutOfStorageException(e);
        } finally {
            try {
                if (null != fos)
                    fos.close();
            } catch (Exception e) {
                logger.error("Can't close FileOutputStream " + e.getMessage(), e);
            }
        }
    }

    /**
     * From a list of files (each being one part of the multipart upload), concatentate all files into a single
     * object that can be accessed by normal S3 calls.  This function could take a long time since a multipart is
     * allowed to have upto 10,000 parts (each 5 gib long).   Amazon defines that while this operation is in progress
     * whitespace is sent back to the client inorder to keep the HTTP connection alive.
     *
     * @param mountedRoot - where both the source and dest buckets are located
     * @param destBucket - resulting location of the concatenated objects
     * @param fileName - resulting file name of the concatenated objects
     * @param sourceBucket - special bucket used to save uploaded file parts
     * @param parts - an array of file names in the sourceBucket
     * @param client - if not null, then keep the servlet connection alive while this potentially long concatentation takes place
     * @return OrderedPair with the first value the MD5 of the final object, and the second value the length of the final object
     */
    @Override
    public OrderedPair<String, Long> concatentateObjects(String mountedRoot, String destBucket, String fileName, String sourceBucket, S3MultipartPart[] parts,
        OutputStream client) {
        MessageDigest md5;
        long totalLength = 0;

        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            logger.error("Unexpected exception " + e.getMessage(), e);
            throw new InternalErrorException("Unable to get MD5 MessageDigest", e);
        }

        File file = new File(getBucketFolderDir(mountedRoot, destBucket) + File.separatorChar + fileName);
        try {
            // -> when versioning is off we need to rewrite the file contents
            file.delete();
            file.createNewFile();

            final FileOutputStream fos = new FileOutputStream(file);
            byte[] buffer = new byte[4096];

            // -> get the input stream for the next file part
            for (int i = 0; i < parts.length; i++) {
                DataHandler nextPart = loadObject(mountedRoot, sourceBucket, parts[i].getPath());
                InputStream is = nextPart.getInputStream();

                int len = 0;
                while ((len = is.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                    md5.update(buffer, 0, len);
                    totalLength += len;
                }
                is.close();

                // -> after each file write tell the client we are still here to keep connection alive
                if (null != client) {
                    client.write(new String(" ").getBytes());
                    client.flush();
                }
            }
            fos.close();
            return new OrderedPair<String, Long>(StringHelper.toHexString(md5.digest()), new Long(totalLength));
            //Create an ordered pair whose first element is the MD4 digest as a (lowercase) hex String
        } catch (IOException e) {
            logger.error("concatentateObjects unexpected exception " + e.getMessage(), e);
            throw new OutOfStorageException(e);
        }
    }

    @Override
    public DataHandler loadObject(String mountedRoot, String bucket, String fileName) {
        File file = new File(getBucketFolderDir(mountedRoot, bucket) + File.separatorChar + fileName);
        try {
            return new DataHandler(file.toURL());
        } catch (MalformedURLException e) {
            throw new FileNotExistException("Unable to open underlying object file");
        }
    }

    @Override
    public void deleteObject(String mountedRoot, String bucket, String fileName) {
        String filePath = new String(getBucketFolderDir(mountedRoot, bucket) + File.separatorChar + fileName);
        File file = new File(filePath);
        if (!file.delete()) {
            logger.error("file: " + filePath + ", f=" + file.isFile() + ", h=" + file.isHidden() + ", e=" + file.exists() + ", w=" + file.canWrite());
            throw new OutOfStorageException("Unable to delete " + filePath + " for object deletion");
        }
    }

    @Override
    public DataHandler loadObjectRange(String mountedRoot, String bucket, String fileName, long startPos, long endPos) {
        File file = new File(getBucketFolderDir(mountedRoot, bucket) + File.separatorChar + fileName);
        try {
            DataSource ds = new FileRangeDataSource(file, startPos, endPos);
            return new DataHandler(ds);
        } catch (MalformedURLException e) {
            throw new FileNotExistException("Unable to open underlying object file");
        } catch (IOException e) {
            throw new FileNotExistException("Unable to open underlying object file");
        }
    }

    public static boolean deleteDirectory(File path) {
        if (path.exists()) {
            File[] files = path.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    deleteDirectory(files[i]);
                } else {
                    files[i].delete();
                }
            }
        }
        return path.delete();
    }

    private String getBucketFolderName(String bucket) {
        // temporary
        String name = bucket.replace(' ', '_');
        name = bucket.replace('\\', '-');
        name = bucket.replace('/', '-');

        return name;
    }
}
TOP

Related Classes of com.cloud.bridge.io.S3FileSystemBucketAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.