com.aliyun.odps.volume.VolumeFSClient.java Source code

Java tutorial

Introduction

Here is the source code for com.aliyun.odps.volume.VolumeFSClient.java

Source

/**
 * 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.aliyun.odps.volume;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;

import com.aliyun.odps.Odps;
import com.aliyun.odps.OdpsException;
import com.aliyun.odps.Volume;
import com.aliyun.odps.VolumeException;
import com.aliyun.odps.VolumeFSFile;
import com.aliyun.odps.Volumes;
import com.aliyun.odps.fs.VolumeFileSystemConfigKeys;
import com.aliyun.odps.tunnel.TunnelException;
import com.aliyun.odps.tunnel.VolumeFSErrorCode;
import com.aliyun.odps.tunnel.VolumeFSTunnel;
import com.aliyun.odps.tunnel.io.CompressOption;
import com.aliyun.odps.tunnel.io.VolumeOutputStream;
import com.aliyun.odps.volume.protocol.VolumeFSConstants;
import com.aliyun.odps.volume.protocol.VolumeFSErrorMessageGenerator;

/**
 * Client for Volume
 * 
 * @author Emerson Zhao [mailto:zhenyi.zzy@alibaba-inc.com]
 *
 */
public class VolumeFSClient {

    private Odps odps;

    private String tunnelEndpoint;

    private Configuration conf;

    public VolumeFSClient(Odps odps, String project, String serviceEndpoint, String tunnelEndpoint,
            Configuration conf) {
        this.odps = odps;
        this.odps.setDefaultProject(project);
        this.odps.setEndpoint(serviceEndpoint);
        this.tunnelEndpoint = tunnelEndpoint;
        this.conf = conf;
    }

    /**
     * Get File meta info
     * 
     * @param path
     * @throws VolumeException
     */
    public VolumeFSFile getFileInfo(String path) throws VolumeException {

        return new VolumeFSJobRunnerProxy<VolumeFSFile>() {

            @Override
            public VolumeFSFile doJob(String path, Map<String, Object> params) throws VolumeException {

                VolumeFSFile file = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path)).getVolumeFSFile(path);
                try {
                    file.reload();
                } catch (OdpsException e) {
                    throw new VolumeException(e);
                }
                return file;
            }

            @Override
            public VolumeFSFile onVolumeMissing(String path, Map<String, Object> params) throws VolumeException {
                VolumeFSFile file = VolumeFSFile.getRoot(odps.getDefaultProject(), odps);
                return file;
            }

            @Override
            public VolumeFSFile onNoSuchVolume(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                        VolumeFSErrorMessageGenerator.noSuchFileOrDirectory(path));
            }

            @Override
            public VolumeFSFile onInvalidPath(String path, Map<String, Object> params) throws VolumeException {
                if (VolumeFSUtil.checkPathIsJustVolume(path)) {
                    try {
                        if (!odps.volumes().exists(VolumeFSUtil.getVolumeFromPath(path))) {
                            throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                                    VolumeFSErrorMessageGenerator.noSuchFileOrDirectory(path));
                        }
                    } catch (OdpsException e) {
                        throw new VolumeException(e);
                    }
                    Volume v = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path));
                    if (isNewVolume(v)) {
                        return VolumeFSFile.transferVolumeToVolumeFSFile(odps.getDefaultProject(), v,
                                odps.getRestClient());
                    } else {
                        throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                                VolumeFSErrorMessageGenerator.oldVolumeAlert(VolumeFSUtil.getVolumeFromPath(path)));
                    }
                } else {
                    throw (VolumeException) params.get(CUR_EXCEPTION);
                }
            }

        }.run(path, null);

    }

    /**
     * Make directory
     * 
     * @param path
     * @throws VolumeException
     */
    public boolean mkdirs(String path) throws VolumeException {

        return new VolumeFSJobRunnerProxy<Boolean>() {

            @Override
            public Boolean doJob(String path, Map<String, Object> params) throws VolumeException {

                VolumeFSFile.create(odps.getDefaultProject(), path, true, odps.getRestClient());
                return true;
            }

            @Override
            public Boolean onVolumeMissing(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.PathAlreadyExists,
                        VolumeFSErrorMessageGenerator.fileExists(path));
            }

            @Override
            public Boolean onNoSuchVolume(String path, Map<String, Object> params) throws VolumeException {

                try {
                    odps.volumes().create(VolumeFSUtil.getVolumeFromPath(path),
                            VolumeFSConstants.CREATE_VOLUME_COMMENT, Volume.Type.NEW);
                } catch (Exception e) {
                    throw new VolumeException(e);
                }
                if (VolumeFSUtil.checkPathIsJustVolume(path)) {
                    return true;
                } else {
                    return doJob(path, params);
                }
            }

            @Override
            public Boolean onInvalidPath(String path, Map<String, Object> params) throws VolumeException {
                // If the path is a volume name , will return true
                if (VolumeFSUtil.checkPathIsJustVolume(path)) {
                    try {
                        if (!odps.volumes().exists(VolumeFSUtil.getVolumeFromPath(path))) {
                            odps.volumes().create(VolumeFSUtil.getVolumeFromPath(path),
                                    VolumeFSConstants.CREATE_VOLUME_COMMENT, Volume.Type.NEW);
                        }
                    } catch (OdpsException e) {
                        throw new VolumeException(e);
                    }
                    return true;
                } else {
                    throw (VolumeException) params.get(CUR_EXCEPTION);
                }
            }

            @Override
            public Boolean onPathAlreadyExists(String path, Map<String, Object> params) throws VolumeException {
                VolumeFSFile file = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path)).getVolumeFSFile(path);
                try {
                    file.reload();
                } catch (OdpsException oe) {
                    throw new VolumeException(oe);
                }
                if (Boolean.TRUE.equals(file.getIsdir())) {
                    return Boolean.TRUE;
                } else {
                    throw (VolumeException) params.get(CUR_EXCEPTION);
                }
            }

        }.run(path, null);
    }

    /**
     * Set file's replication
     * 
     * @param path
     * @param replication
     * @throws VolumeException
     */
    public boolean setReplication(String path, short replication) throws VolumeException {

        Map<String, Object> params = new HashMap<String, Object>();
        Map<String, String> innerParams = new HashMap<String, String>();

        innerParams.put(VolumeFSFile.ParamKey.REPLICATION.name().toLowerCase(), String.valueOf(replication));
        params.put("params", innerParams);

        return new VolumeFSJobRunnerProxy<Boolean>() {

            @SuppressWarnings("unchecked")
            @Override
            public Boolean doJob(String path, Map<String, Object> params) throws VolumeException {

                VolumeFSFile file = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path)).getVolumeFSFile(path);
                file.update((Map<String, String>) params.get("params"));
                return true;
            }

            @Override
            public Boolean onVolumeMissing(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.NotAcceptableOperation,
                        VolumeFSErrorMessageGenerator.isADirectory(path));
            }

            @Override
            public Boolean onNoSuchVolume(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                        VolumeFSErrorMessageGenerator.noSuchFileOrDirectory(path));
            }
        }.run(path, params);

    }

    /**
     * List files (and directories) in specific path
     * 
     * @param path
     * @throws VolumeException
     */
    public VolumeFSFile[] getFileInfosByPath(String path) throws VolumeException {

        return new VolumeFSJobRunnerProxy<VolumeFSFile[]>() {

            @Override
            public VolumeFSFile[] doJob(String path, Map<String, Object> params) throws VolumeException {

                VolumeFSFile file = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path)).getVolumeFSFile(path);
                Iterator<VolumeFSFile> iterator = file.iterator();
                List<VolumeFSFile> files = new ArrayList<VolumeFSFile>();
                try {
                    while (iterator.hasNext()) {
                        files.add(iterator.next());
                    }
                } catch (RuntimeException e) {
                    if (e.getCause() instanceof VolumeException) {
                        throw (VolumeException) e.getCause();
                    } else {
                        throw new VolumeException(e);
                    }
                }
                return files.toArray(new VolumeFSFile[0]);
            }

            @Override
            public VolumeFSFile[] onVolumeMissing(String path, Map<String, Object> params) throws VolumeException {
                List<VolumeFSFile> files = new ArrayList<VolumeFSFile>();
                Volumes volumes = odps.volumes();
                Iterator<Volume> it = volumes.iterator();
                while (it.hasNext()) {
                    Volume v = it.next();
                    if (isNewVolume(v)) {
                        files.add(VolumeFSFile.transferVolumeToVolumeFSFile(odps.getDefaultProject(), v,
                                odps.getRestClient()));
                    }
                }
                return files.toArray(new VolumeFSFile[0]);
            }

            @Override
            public VolumeFSFile[] onNoSuchVolume(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                        VolumeFSErrorMessageGenerator.noSuchFileOrDirectory(path));
            }

            @Override
            public VolumeFSFile[] onInvalidPath(String path, Map<String, Object> params) throws VolumeException {
                if (VolumeFSUtil.checkPathIsJustVolume(path)) {
                    return doJob(path, params);
                } else {
                    throw (VolumeException) params.get(CUR_EXCEPTION);
                }
            }
        }.run(path, null);

    }

    /**
     * Rename file (or directory)
     * 
     * @param src
     * @param dst
     * @throws VolumeException
     */
    public boolean rename(String src, String dst) throws VolumeException {

        Map<String, Object> params = new HashMap<String, Object>();
        Map<String, String> innerParams = new HashMap<String, String>();
        innerParams.put(VolumeFSFile.ParamKey.PATH.name().toLowerCase(), dst);
        params.put("params", innerParams);

        return new VolumeFSJobRunnerProxy<Boolean>() {

            @SuppressWarnings("unchecked")
            @Override
            public Boolean doJob(String path, Map<String, Object> params) throws VolumeException {

                VolumeFSFile file = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path)).getVolumeFSFile(path);
                file.update((Map<String, String>) params.get("params"));
                return true;
            }

            @Override
            public Boolean onVolumeMissing(String path, Map<String, Object> params) throws VolumeException {
                return false;
            }

            @Override
            public Boolean onNoSuchVolume(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                        VolumeFSErrorMessageGenerator.noSuchFileOrDirectory(path));
            }

            @Override
            public Boolean onInvalidPath(String path, Map<String, Object> params) throws VolumeException {
                if (VolumeFSUtil.checkPathIsJustVolume(path)) {
                    throw new VolumeException(VolumeFSErrorCode.NotAcceptableOperation,
                            VolumeFSErrorMessageGenerator
                                    .theOpreationIsNotAllowed("mv volume (The first level directory)"));
                } else {
                    throw (VolumeException) params.get(CUR_EXCEPTION);
                }
            }
        }.run(src, params);

    }

    /**
     * Delete file (or directory)
     * 
     * @param path
     * @param recursive
     * @throws VolumeException
     */
    public boolean delete(String path, boolean recursive) throws VolumeException {

        Map<String, Object> params = new HashMap<String, Object>();
        params.put(VolumeFSFile.ParamKey.RECURSIVE.name().toLowerCase(), recursive);

        return new VolumeFSJobRunnerProxy<Boolean>() {

            @Override
            public Boolean doJob(String path, Map<String, Object> params) throws VolumeException {

                VolumeFSFile file = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path)).getVolumeFSFile(path);
                file.delete((Boolean) params.get(VolumeFSFile.ParamKey.RECURSIVE.name().toLowerCase()));
                return true;
            }

            @Override
            public Boolean onVolumeMissing(String path, Map<String, Object> params) throws VolumeException {
                return false;
            }

            @Override
            public Boolean onNoSuchVolume(String path, Map<String, Object> params) throws VolumeException {
                throw new VolumeException(VolumeFSErrorCode.NoSuchPath,
                        VolumeFSErrorMessageGenerator.noSuchFileOrDirectory(path));
            }

            @Override
            public Boolean onInvalidPath(String path, Map<String, Object> params) throws VolumeException {
                // If the path is a volume name , will call volume API
                if (VolumeFSUtil.checkPathIsJustVolume(path)) {
                    try {
                        Volume v = odps.volumes().get(VolumeFSUtil.getVolumeFromPath(path));
                        if (isNewVolume(v)) {
                            odps.volumes().delete(VolumeFSUtil.getVolumeFromPath(path));
                            return true;
                        } else {
                            throw new VolumeException(VolumeFSErrorCode.NoSuchPath, VolumeFSErrorMessageGenerator
                                    .oldVolumeAlert(VolumeFSUtil.getVolumeFromPath(path)));
                        }
                    } catch (OdpsException e) {
                        throw new VolumeException(e);
                    }
                } else {
                    throw (VolumeException) params.get(CUR_EXCEPTION);
                }
            }

        }.run(path, params);

    }

    /**
     * Download volume file
     * 
     * @param path
     * @param start start of range
     * @param end start of range
     * @param targetFile
     * @param append
     * @throws IOException
     */
    public void downloadFile(String path, Long start, Long end, File targetFile, boolean append)
            throws VolumeException {
        VolumeFSUtil.checkPath(path);
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(targetFile, append);
        } catch (FileNotFoundException e) {
            throw new VolumeException(e);
        }
        InputStream inputStream = null;
        try {
            inputStream = openInputStream(path, start, end);
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            throw new VolumeException(e);
        } finally {
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(inputStream);
        }
    }

    /**
     * Create an {@link InputStream} at the indicated Path
     * 
     * @param path
     * @param start
     * @param end
     * @throws IOException
     */
    public InputStream openInputStream(String path, Long start, Long end) throws VolumeException {
        VolumeFSUtil.checkPath(path);
        boolean compress = conf.getBoolean(VolumeFileSystemConfigKeys.ODPS_VOLUME_TRANSFER_COMPRESS_ENABLED, false);
        CompressOption compressOption = null;
        if (compress) {
            CompressOption.CompressAlgorithm a = CompressOption.CompressAlgorithm
                    .valueOf(conf.get(VolumeFileSystemConfigKeys.ODPS_VOLUME_TRANSFER_COMPRESS_ALGORITHM,
                            VolumeFSConstants.ODPS_ZLIB));
            compressOption = new CompressOption(a, 1, 0);
        }
        InputStream in = null;
        try {
            in = getVolumeTunnel().openInputStream(odps.getDefaultProject(), path, start, end, compressOption);
        } catch (TunnelException e) {
            throw new VolumeException(e);
        }
        return in;
    }

    /**
     * Create an {@link OutputStream} at the indicated Path
     * 
     * @param path
     * @param overwrite
     * @param replication
     * @throws IOException
     */
    public OutputStream openOutputStream(String path, boolean overwrite, short replication) throws VolumeException {
        VolumeFSUtil.checkPath(path);
        VolumeFSFile file = null;
        try {
            if (!odps.volumes().exists(VolumeFSUtil.getVolumeFromPath(path))) {
                odps.volumes().create(VolumeFSUtil.getVolumeFromPath(path), VolumeFSConstants.CREATE_VOLUME_COMMENT,
                        Volume.Type.NEW);
            }
        } catch (OdpsException oe) {
            throw new VolumeException(oe);
        }
        try {
            file = getFileInfo(path);
        } catch (VolumeException e) {
            if (!VolumeFSErrorCode.NoSuchPath.equalsIgnoreCase(e.getErrCode())) {
                throw e;
            }
        }
        if (file != null) {
            if (overwrite) {
                if (file.getIsdir()) {
                    throw new VolumeException(VolumeFSErrorCode.PathAlreadyExists,
                            VolumeFSErrorMessageGenerator.fileExists(path));
                } else {
                    delete(path, true);
                }
            } else {
                throw new VolumeException(VolumeFSErrorCode.PathAlreadyExists,
                        VolumeFSErrorMessageGenerator.fileExists(path));
            }
        }
        VolumeFSTunnel tunnel = getVolumeTunnel();
        // create a zero-byte file to satisfy visibility before wrote data into file
        createZeroByteFile(path, replication, tunnel);
        boolean compress = conf.getBoolean(VolumeFileSystemConfigKeys.ODPS_VOLUME_TRANSFER_COMPRESS_ENABLED, false);
        CompressOption compressOption = null;
        if (compress) {
            CompressOption.CompressAlgorithm a = CompressOption.CompressAlgorithm
                    .valueOf(conf.get(VolumeFileSystemConfigKeys.ODPS_VOLUME_TRANSFER_COMPRESS_ALGORITHM,
                            VolumeFSConstants.ODPS_ZLIB));
            compressOption = new CompressOption(a, 1, 0);
        }
        OutputStream out = null;
        try {
            out = tunnel.openOutputStream(odps.getDefaultProject(), path, new Integer(replication), compressOption);
        } catch (TunnelException e) {
            throw new VolumeException(e);
        }
        return out;
    }

    /**
     * Create a zero-byte file
     * 
     * @param path
     * @param replication
     * @param tunnel
     * @throws VolumeException
     */
    public void createZeroByteFile(String path, short replication, VolumeFSTunnel tunnel) throws VolumeException {
        VolumeFSUtil.checkPath(path);
        OutputStream o = null;
        try {
            o = tunnel.openOutputStream(odps.getDefaultProject(), path, new Integer(replication),
                    new CompressOption());
            String sessionId = VolumeFSTunnel.getUploadSessionId((VolumeOutputStream) o);
            tunnel.commit(odps.getDefaultProject(), path, sessionId);
        } catch (TunnelException e) {
            throw new VolumeException(e);
        }
    }

    /**
     * Commit the uploadSession
     * 
     * @param path
     * @param out
     * @param overwrite
     * @throws VolumeException
     */
    public void commitUploadSession(String path, VolumeOutputStream out, boolean overwrite) throws VolumeException {
        VolumeFSUtil.checkPath(path);
        try {
            out.close();
            String sessionId = VolumeFSTunnel.getUploadSessionId(out);
            VolumeFSFile file = null;
            try {
                file = getFileInfo(path);
            } catch (Exception e) {
            }
            if (file != null) {
                if (overwrite) {
                    if (file.getIsdir()) {
                        throw new VolumeException(VolumeFSErrorCode.PathAlreadyExists,
                                VolumeFSErrorMessageGenerator.fileExists(path));
                    } else {
                        file.delete(true);
                    }
                } else {
                    if (file.getLength().longValue() == 0) {
                        // Delete zero-byte file
                        file.delete(true);
                    } else {
                        throw new VolumeException(VolumeFSErrorCode.PathAlreadyExists,
                                VolumeFSErrorMessageGenerator.fileExists(path));
                    }
                }
            }
            getVolumeTunnel().commit(odps.getDefaultProject(), path, sessionId);
        } catch (Exception e) {
            throw new VolumeException(e);
        }
    }

    private VolumeFSTunnel getVolumeTunnel() {
        VolumeFSTunnel tunnelFS = new VolumeFSTunnel(odps.clone());
        if (tunnelEndpoint != null)
            tunnelFS.setEndpoint(tunnelEndpoint);
        return tunnelFS;
    }

    private boolean isNewVolume(Volume v) {
        return Volume.Type.NEW.equals(v.getType());
    }
}