Package com.asakusafw.cleaner.main

Source Code of com.asakusafw.cleaner.main.HDFSCleaner

/**
* Copyright 2011-2014 Asakusa Framework Team.
*
* 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 com.asakusafw.cleaner.main;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.util.Tool;

import com.asakusafw.cleaner.bean.DFSCleanerBean;
import com.asakusafw.cleaner.common.CleanerInitializer;
import com.asakusafw.cleaner.common.ConfigurationLoader;
import com.asakusafw.cleaner.common.Constants;
import com.asakusafw.cleaner.common.MessageIdConst;
import com.asakusafw.cleaner.exception.CleanerSystemException;
import com.asakusafw.cleaner.log.Log;

/**
* HDFSCleanerの実行クラス。
* @author yuta.shirai
*
*/
public class HDFSCleaner extends Configured implements Tool {
    /** クラス。 */
    private static final Class<?> CLASS = HDFSCleaner.class;



    /**
     * Creates a new instance.
     */
    public HDFSCleaner() {
        super();
    }

    /**
     * Creates a new instance with configuration object.
     * @param conf configuration
     */
    public HDFSCleaner(Configuration conf) {
        super(conf);
    }

    /**
     * メインメソッド
     *
     * コマンドライン引数として以下の値をとる。
     * ・args[0]=動作モード
     * ・args[1]=ユーザー名
     * ・args[2]=コンフィグレーションファイル
     *
     * @param args コマンドライン引数
     * @throws Exception if failed to execute
     */
    public static void main(String[] args) throws Exception {
        HDFSCleaner tool = new HDFSCleaner();
        int result = tool.run(args);
        System.exit(result);
    }

    @Override
    public int run(String[] args) throws Exception {
        return execute(args);
    }

    /**
     * HDFSCleanerの処理を実行する。
     * @param args コマンドライン引数
     * @return 終了ステータス
     */
    protected int execute(String[] args) {
        String[] prop = new String[1];
        String mode = null;
        String user = null;
        FileSystem fs = null;

        if (args.length > 0) {
            mode = args[0];
        }
        if (args.length > 1) {
            user = args[1];
        }
        if (args.length > 2) {
            prop[0] = args[2];
        }

        // 引数の数をチェック
        if (args.length != 3) {
            System.err.println(
                    "ERROR:引数の数が不正です。 引数の数:" + args.length
                    + " 動作モード:" + mode
                    + " ユーザー名:" + user
                    + " コンフィグレーションファイル:" + prop[0]);
            Log.log(CLASS, MessageIdConst.HCLN_PARAMCHECK_ERROR, "引数の数", args.length, new Date(), mode, prop[0]);
            return Constants.EXIT_CODE_ERROR;
        }

        try {
            // 初期処理
            if (!CleanerInitializer.initDFSCleaner(prop)) {
                Log.log(CLASS, MessageIdConst.HCLN_INIT_ERROR, new Date(), mode, prop[0]);
                return Constants.EXIT_CODE_ERROR;
            }

            // 開始ログ出力
            Log.log(CLASS, MessageIdConst.HCLN_START, new Date(), mode, prop[0]);

            // 動作モードを取得
            boolean recursive = false;
            if (Constants.CLEAN_MODE_NOMAL.equals(mode)) {
                recursive = false;
            } else if (Constants.CLEAN_MODE_RECURSIVE.equals(mode)) {
                recursive = true;
            } else {
                Log.log(CLASS, MessageIdConst.HCLN_PARAMCHECK_ERROR, "動作モード", mode, new Date(), mode, prop[0]);
                return Constants.EXIT_CODE_ERROR;
            }

            // HDFS上のクリーニング対象ディレクトリを取得
            DFSCleanerBean[] bean = null;
            try {
                bean = getCleanLocalPath(user);
            } catch (CleanerSystemException e) {
                Log.log(e.getCause(), e.getClazz(), e.getMessageId(), e.getMessageArgs());
                return Constants.EXIT_CODE_ERROR;
            }

            // 保持期間を取得
            int keepDate = getHDFSFileKeepDate();

            boolean cleanResult = true;
            Date now = new Date();
            for (int i = 0; i < bean.length; i++) {
                try {
                    // クリーニングを実行
                    Path cleanDir = bean[i].getCleanDir();
                    // ファイルシステムを取得
                    try {
                        Configuration conf = getConf();
                        fs = cleanDir.getFileSystem(conf);
                        if (fs == null) {
                            Log.log(CLASS, MessageIdConst.HCLN_CLEN_DIR_ERROR,
                                    "Path.getFileSystemの戻り値がnull", cleanDir.toString());
                            cleanResult = false;
                            continue;
                        }
                    } catch (IOException e) {
                        Log.log(e, CLASS, MessageIdConst.HCLN_CLEN_DIR_ERROR,
                                "HDFSのファイルシステムの取得に失敗", cleanDir.toString());
                        cleanResult = false;
                        continue;
                    }

                    boolean target = bean[i].hasExecutionId();
                    String pattern = bean[i].getPattern();
                    Log.log(
                            CLASS,
                            MessageIdConst.HCLN_CLEN_FILE,
                            cleanDir.toString(),
                            pattern,
                            keepDate,
                            mode,
                            target,
                            now);
                    if (cleanDir(fs, cleanDir, target, pattern, keepDate, now, recursive)) {
                        Log.log(CLASS, MessageIdConst.HCLN_CLEN_DIR_SUCCESS, cleanDir.toString(), keepDate, mode);
                    } else {
                        Log.log(CLASS, MessageIdConst.HCLN_CLEN_DIR_FAIL, cleanDir.toString(), keepDate, mode);
                        cleanResult = false;
                    }
                } catch (CleanerSystemException e) {
                    Log.log(e.getCause(), e.getClazz(), e.getMessageId(), e.getMessageArgs());
                    cleanResult = false;
                } finally {
                    if (fs != null) {
                        try {
                            fs.close();
                        } catch (IOException ignored) {
                            // ignored
                        }
                    }
                }
            }

            // 正常終了
            if (cleanResult) {
                Log.log(CLASS, MessageIdConst.HCLN_EXIT_SUCCESS, new Date(), mode, prop[0]);
                return Constants.EXIT_CODE_SUCCESS;
            } else {
                Log.log(CLASS, MessageIdConst.HCLN_EXIT_WARNING, new Date(), mode, prop[0]);
                return Constants.EXIT_CODE_WARNING;
            }
        } catch (RuntimeException e) {
            try {
                Log.log(e, CLASS, MessageIdConst.HCLN_EXCEPRION, new Date(), mode, prop[0]);
                return Constants.EXIT_CODE_ERROR;
            } catch (Exception e1) {
                System.err.print("HDFSCleanerで不明なエラーが発生しました。");
                e1.printStackTrace();
                return Constants.EXIT_CODE_ERROR;
            }
        }
    }
    /**
     * クリーニング対象ディレクトリをクリーニングする。
     * @param fs HDFSを表すファイルシステム
     * @param cleanPath HDFS上のクリーニング対象ディレクトリのパス
     * @param isSetExecutionId ジョブフロー実行IDが指定されているかどうか
     * @param pattern クリーニングパターン
     * @param keepDate 保持日数
     * @param now 現在日時
     * @param recursive 再帰的にクリーニングを行うか
     * @return クリーニング結果
     * @throws CleanerSystemException
     */
    private boolean cleanDir(
            FileSystem fs,
            Path cleanPath,
            boolean isSetExecutionId,
            String pattern,
            int keepDate,
            Date now,
            boolean recursive) throws CleanerSystemException {
        try {
            if (!fs.exists(cleanPath)) {
                // 指定されたディレクトリが存在しない
                Log.log(CLASS, MessageIdConst.HCLN_CLEN_DIR_ERROR, "指定されたディレクトリが存在しない", cleanPath.toString());
                return false;
            }
            if (!fs.getFileStatus(cleanPath).isDir()) {
                // 指定されたパスがディレクトリでない
                Log.log(CLASS, MessageIdConst.HCLN_CLEN_DIR_ERROR, "指定されたパスがディレクトリでない", cleanPath.toString());
                return false;
            }

            // クリーニングを行う
            Log.log(CLASS, MessageIdConst.HCLN_FILE_DELETE, cleanPath.toString());
            int cleanFileCount = 0;
            int cleanDirCount = 0;
            boolean result = true;
            FileStatus[] dirStatus = getListStatus(fs, cleanPath);
            Path[] listedPaths = FileUtil.stat2Paths(dirStatus);
            for (Path path : listedPaths) {
                FileStatus status = fs.getFileStatus(path);
                long lastModifiedTime = status.getModificationTime();
                if (status.isDir() && recursive) {
                    // ディレクトリかつ、再帰的に処理を行う場合
                    if (isSetExecutionId) {
                        // ジョブフロー実行IDが指定されている場合はMMに問い合わせを行う
                        String executionId = path.getName();
                        if (isRunningJobFlow(executionId)) {
                            // 実行中のジョブフローの為、クリーニング対象としない。
                            Log.log(CLASS, MessageIdConst.HCLN_CLEN_DIR_EXEC, path.toString());
                            continue;
                        }
                    }
                    FileStatus[] childdirStatus = getListStatus(fs, path);
                    if (childdirStatus.length == 0) {
                        // 子ファイルが存在しない場合、ディレクトリを削除
                        if (isExpired(lastModifiedTime, keepDate, now)) {
                            if (!fs.delete(path, false)) {
                                Log.log(CLASS, MessageIdConst.HCLN_CLEN_FAIL, "ディレクトリ", path.toString());
                                result = false;
                            } else {
                                cleanDirCount++;
                                Log.log(CLASS, MessageIdConst.HCLN_DIR_DELETE, path.toString());
                            }
                        }
                    } else {
                        // 子ファイルが存在する場合、再帰的にクリーニング処理を行う。
                        if (cleanDir(fs, path, false, pattern, keepDate, now, recursive)) {
                            // 子ファイルが全て削除された場合はディレクトリを削除する
                            childdirStatus = getListStatus(fs, path);
                            if (childdirStatus.length == 0) {
                                if (isExpired(lastModifiedTime, keepDate, now)) {
                                    if (!fs.delete(path, false)) {
                                        Log.log(CLASS, MessageIdConst.HCLN_CLEN_FAIL, "ディレクトリ", path.toString());
                                        result = false;
                                    } else {
                                        cleanDirCount++;
                                        Log.log(CLASS, MessageIdConst.HCLN_DIR_DELETE, path.toString());
                                    }
                                }
                            }
                        } else {
                            Log.log(CLASS, MessageIdConst.HCLN_CLEN_FAIL, "ディレクトリ", path.toString());
                            result = false;
                        }
                    }
                } else if (!status.isDir()) {
                    // ファイルの場合、保持期間を過ぎていれば削除する
                    if (isExpired(lastModifiedTime, keepDate, now) && isMatchPattern(path, pattern)) {
                        if (!fs.delete(path, false)) {
                            Log.log(CLASS, MessageIdConst.HCLN_CLEN_FAIL, "ファイル", path.toString());
                            result = false;
                        } else {
                            Log.log(CLASS, MessageIdConst.HCLN_DELETE_FILE, path.toString());
                            cleanFileCount++;
                        }
                    }
                }
            }

            Log.log(
                    CLASS,
                    MessageIdConst.HCLN_FILE_DELETE_SUCCESS,
                    cleanPath.toString(),
                    cleanDirCount,
                    cleanFileCount);

            return result;
        } catch (IOException e) {
            Log.log(e, CLASS, MessageIdConst.HCLN_CLEN_DIR_EXCEPTION, cleanPath.getName());
            return false;
        }
    }
    /**
     * 子ファイルを取得する。
     * @param fs ファイルシステム
     * @param path 親ディレクトリ
     * @return 子ファイル
     * @throws IOException
     */
    private FileStatus[] getListStatus(FileSystem fs, Path path) throws IOException {
        FileStatus[] status;
        try {
            status = fs.listStatus(path);
        } catch (FileNotFoundException e) {
            status = null;
        }
        if (status == null) {
            status = new FileStatus[0];
        }
        return status;
    }
    /**
     * 削除対象のファイルがパターンにマッチするか判断する。
     * @param path 削除対象ファイル
     * @param pattern 削除対象ファイルパターン
     * @return 削除可否
     * @throws CleanerSystemException
     */
    private boolean isMatchPattern(Path path, String pattern) throws CleanerSystemException {
        if (pattern == null || pattern.equals("")) {
            return true;
        } else {
            String strFile = path.toString();
            try {
                Matcher m = Pattern.compile(pattern).matcher(strFile);
                return m.matches();
            } catch (PatternSyntaxException e) {
                throw new CleanerSystemException(e, this.getClass(), MessageIdConst.HCLN_PATTERN_FAIL, pattern);
            }
        }
    }
    /**
     * 保持日付を判断し、ファイルが削除可能か判断する。
     * @param lastModifiedTime ファイルの最終更新日時
     * @param keepDate 保持日数
     * @param now 現在日時
     * @return 削除可否
     */
    private boolean isExpired(long lastModifiedTime, int keepDate, Date now) {
        long keepTime = (keepDate) * 24L * 60L * 60L * 1000L;
        long period = lastModifiedTime + keepTime;
        return now.getTime() > period;
    }
    /**
     * 当該ジョブフローインスタンスを実行中か問い合わせる。
     * @param executionId ジョブフロー実行ID
     * @return 実行中の場合:true、実行中でない場合:false
     */
    protected boolean isRunningJobFlow(String executionId) {
        // TODO 未実装状態。要実装。
        return false;
    }
    /**
     * プロパティからクリーニング対象ディレクトリの保持日数を取得する。
     * @return クリーニング対象ディレクトリの保持日数
     */
    private int getHDFSFileKeepDate() {
        return Integer.parseInt(ConfigurationLoader.getProperty(Constants.PROP_KEY_HDFS_FILE_KEEP_DATE));
    }
    /**
     * プロパティからクリーニング対象パスを取得し、
     * HDFS上のディレクトリパスをフルパスにして返す。
     * @param user ユーザー名
     * @return クリーニング対象パス
     * @throws CleanerSystemException クリーニングパターンが存在しない場合
     */
    private DFSCleanerBean[] getCleanLocalPath(String user) throws CleanerSystemException {
        // プロパティからクリーニング対象パスを取得する
        List<String> cleanDirList = ConfigurationLoader.getPropStartWithString(
                Constants.PROP_KEY_HDFS_FILE_CLEAN_DIR + ".");
        List<String> noEmptyDirList = ConfigurationLoader.getNoEmptyList(cleanDirList);

        List<DFSCleanerBean> list = new ArrayList<DFSCleanerBean>();
        int listSize = noEmptyDirList.size();
        for (int i = 0; i < listSize; i++) {
            // クリーニングディレクトリを設定
            DFSCleanerBean bean = new DFSCleanerBean();
            String dirKey = noEmptyDirList.get(i);
            // ユーザー名を表す文字列を置換
            String strPath = ConfigurationLoader.getProperty(dirKey);
            String strCleanPath = strPath.replace(Constants.HDFS_PATH_REPLACE_STR_USER, user);
            // ジョブフロー実行IDを表す文字列を排除
            boolean isSetexecutionId = false;
            if (strCleanPath.endsWith(Constants.HDFS_PATH_REPLACE_STR_ID)) {
                isSetexecutionId = true;
                strCleanPath = strCleanPath.substring(0, strCleanPath.indexOf(Constants.HDFS_PATH_REPLACE_STR_ID) - 1);
            }
            bean.setCleanDir(createPath(strCleanPath));
            bean.setExecutionId(isSetexecutionId);

            // クリーニングパターンを設定
            String number = dirKey.substring(dirKey.lastIndexOf(".") + 1, dirKey.length());
            String pattarnKey = Constants.PROP_KEY_HDFS_FILE_CLEAN_PATTERN + "." + number;
            String pattern = ConfigurationLoader.getProperty(pattarnKey);
            if (pattern == null || pattern.equals("")) {
                throw new CleanerSystemException(
                        this.getClass(),
                        MessageIdConst.HCLN_PATTERN_NOT_FOUND,
                        dirKey,
                        strPath,
                        pattarnKey);
            } else {
                bean.setPattern(pattern);
            }

            list.add(bean);
        }
        return list.toArray(new DFSCleanerBean[list.size()]);
    }
    /**
     * パスをフルパスに組み立てて返す。
     * @param strCleanPath クリーニングパス
     * @return フルパス
     */
    protected Path createPath(String strCleanPath) {
        StringBuffer path = new StringBuffer(ConfigurationLoader.getProperty(Constants.PROP_KEY_HDFS_PROTCOL_HOST));
        path.append(Constants.HDFSFIXED_PATH);
        path.append(strCleanPath);
        return new Path(path.toString());
    }
}
TOP

Related Classes of com.asakusafw.cleaner.main.HDFSCleaner

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.