    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.model;
    017
    018import java.io.ByteArrayInputStream;
    019import java.io.ByteArrayOutputStream;
    020import java.io.File;
    021import java.io.FileFilter;
    022import java.io.FileNotFoundException;
    023import java.io.IOException;
    024import java.io.InputStream;
    025import java.net.URI;
    026import java.util.ArrayList;
    027import java.util.List;
    028import java.util.regex.Matcher;
    029import java.util.regex.Pattern;
    030
    031import org.opengion.fukurou.system.Closer;
    032import org.opengion.fukurou.util.StringUtil;
    033
    034/**
    035 * クラウドストレージ対応用の抽象クラスです。
    036 * 各ベンダーのストレージに対応したプラグインを作成する場合はこのクラスを継承してください。
    037 *
    038 *
    039 * @og.group ファイル操作
    040 *
    041 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
    042 * @og.rev 5.10.9.0 (2019/03/01) 変更対応
    043 * @author oota
    044 * @since JDK7.0
    045 */
    046public abstract class CloudFileOperation extends FileOperation {
    047        //* このプログラムのVERSION文字列を設定します。{@VALUE} */
    048        private static final String VERSION = "7.0.2.1 (2019/03/04)" ;
    049        private static final long serialVersionUID = 702120190304L ;
    050
    051        /** バッファサイズ {@value} */
    052        private static final int BUFFER_SIZE = 1024 * 4;
    053        /** パス */
    054        protected final String conPath;
    055        /** バケット名 */
    056        protected final String conBucket;
    057
    058        private static final String UNIMPLEMNTED_ERR="このクラスでは未実装のメソッドです。";
    059        private static final char   FS = '/' ;
    060        // 5.10.12.2 (2019/06/17) 相対パス対応「../」と１つ前のディレクトリ情報を抽出(１つ前が先頭の場合は、/ではなく^)
    061        // 7.2.9.4 (2020/11/20) PMD:Variables that are final and static should be all capitals, 'ptnPreDir' is not all capitals.
    062//      private static final Pattern ptnPreDir = Pattern.compile("(?<=/|^)[^/]+/\\.\\./");
    063        private static final Pattern PTN_PRE_DIR = Pattern.compile("(?<=/|^)[^/]+/\\.\\./");
    064
    065        /**
    066         * コンストラクタ
    067         *
    068         *
    069         * @param bucket バケット名
    070         * @param inPath ファイルパス
    071         */
    072        public CloudFileOperation(final String bucket, final String inPath) {
    073                super(inPath);
    074
    075                this.conPath = editPath(replaceFileSeparetor(inPath));
    076
    077                this.conBucket = bucket;
    078
    079                if (StringUtil.isNull(conBucket)) {
    080                        final String errMsg = "バケット未指定です。hayabusa利用ではシステム変数の「CLOUD_BUCKET」にバケット名を設定して下さい。";
    081                        throw new RuntimeException(errMsg);
    082                }
    083        }
    084
    085        /**
    086         * データ書き込み
    087         *
    088         * InputStreamのデータを書き込みます。
    089         *
    090         * @param is 書き込みデータのInputStream
    091         * @throws IOException IO関連のエラー情報
    092         */
    093        @Override
    094        public abstract void write(InputStream is) throws IOException;
    095
    096        /**
    097         * データ読み込み
    098         *
    099         * データを読み込み、InputStreamを返します。
    100         *
    101         * @return 読み込みデータのInputStream
    102         * @throws FileNotFoundException ファイル非存在エラー情報
    103         */
    104        @Override
    105        public abstract InputStream read() throws FileNotFoundException;
    106
    107        /**
    108         * ファイル削除
    109         *
    110         * ファイルを削除します。
    111         *
    112         * @return 成否フラグ
    113         */
    114        @Override
    115        public abstract boolean delete();
    116
    117        /**
    118         * ファイルコピー
    119         *
    120         * ファイルを指定先にコピーします。
    121         *
    122         * @param afPath コピー先
    123         * @return 成否フラグ
    124         */
    125        @Override
    126        public abstract boolean copy(String afPath);
    127
    128        /**
    129         * ファイルサイズ取得
    130         *
    131         * ファイルサイズを返します。
    132         *
    133         * @return ファイルサイズ
    134         */
    135        @Override
    136        public abstract long length();
    137
    138        /**
    139         * 最終更新時刻取得
    140         *
    141         * 最終更新時刻を返します。
    142         *
    143         * @return 最終更新時刻
    144         */
    145        @Override
    146        public abstract long lastModified();
    147
    148        /**
    149         * ファイル判定
    150         *
    151         * ファイルの場合は、trueを返します。
    152         *
    153         * @return ファイルフラグ
    154         */
    155        @Override
    156        public abstract boolean isFile();
    157
    158        /**
    159         * ディレクトリ判定
    160         *
    161         * ディレクトリの場合は、trueを返します。
    162         *
    163         * @return ディレクトリフラグ
    164         */
    165        @Override
    166        public abstract boolean isDirectory();
    167
    168        /**
    169         * 一覧取得
    170         *
    171         * パスのファイルと、ディレクトリ一覧を取得します。
    172         *
    173         * @param filter ﾌｧｲﾙﾌｨﾙﾀｰ
    174         * @return ファイルとティレクトリ一覧
    175         */
    176        @Override
    177        public abstract File[] listFiles(FileFilter filter);
    178
    179        /**
    180         * 親ディレクトリの取得
    181         *
    182         * 親のディレクトリ情報を返します。
    183         *
    184         * @return 親のディレクトリ
    185         */
    186        @Override
    187        public abstract File getParentFile();
    188
    189        /**
    190         * ファイルパス取得
    191         *
    192         * ファイルパスを取得します。
    193         *
    194         * @return 設定パス
    195         */
    196        @Override
    197        public String getPath() {
    198                return conPath;
    199        }
    200
    201        /**
    202         * 絶対パス取得
    203         *
    204         * 絶対パスを取得します。
    205         *
    206         * @return 絶対パス
    207         */
    208        @Override
    209        public String getAbsolutePath() {
    210                return conPath;
    211        }
    212
    213        /**
    214         * ファイル名取得
    215         *
    216         * ファイル名を取得します。
    217         *
    218         * @return 名称
    219         */
    220        @Override
    221        public String getName() {
    222                return drawName(conPath);
    223        }
    224
    225        /**
    226         * 親のパス取得
    227         *
    228         * 親のパスを取得します。
    229         *
    230         * @return 親のパス
    231         */
    232        @Override
    233        public String getParent() {
    234                return drawParent(conPath);
    235        }
    236
    237        /**
    238         * ファイル移動
    239         *
    240         * ファイルを指定先に移動します。
    241         *
    242         * @param afPath 移動先
    243         * @return 成否フラグ
    244         */
    245        @Override
    246        public boolean move(final String afPath) {
    247                boolean flgRtn = false;
    248
    249                flgRtn = copy(afPath);
    250                if (flgRtn) {
    251                        flgRtn = delete();
    252                }
    253
    254                return flgRtn;
    255        }
    256
    257        /**
    258         * 存在チェック
    259         *
    260         * 存在する場合は、trueを返します。
    261         *
    262         * @return 存在フラグ
    263         */
    264        @Override
    265        public boolean exists() {
    266                return isDirectory() | isFile();
    267        }
    268
    269        /**
    270         * ディレクトリの作成
    271         *
    272         * ※１つのディレクトリのみ作成します。
    273         * クラウドストレージにはディレクトリの概念が無いため、
    274         * 作成は行わず、trueを返します。
    275         *
    276         * @return 成否フラグ
    277         */
    278        @Override
    279        public boolean mkdir() {
    280                return true;
    281        }
    282
    283        /**
    284         * ディレクトリの作成(複数)
    285         *
    286         * ※複数のディレクトリを作成します。
    287         * クラウドストレージにはディレクトリの概念が無いため、
    288         * 作成は行わず、trueを返します。
    289         *
    290         *
    291         * @return 成否フラグ
    292         */
    293        @Override
    294        public boolean mkdirs() {
    295                return true;
    296        }
    297
    298        /**
    299         * ファイル名変更
    300         *
    301         * 指定のファイル情報のファイル名に変更します。
    302         *
    303         * @param dest 変更後のファイル情報
    304         * @return 成否フラグ
    305         */
    306        @Override
    307        public boolean renameTo(final File dest) {
    308                return move(dest.getPath());
    309        }
    310
    311        /**
    312         * 書き込み可能フラグ
    313         *
    314         * ※クラウドストレージの場合は、
    315         * 存在すればtrueを返します。
    316         *
    317         * @return 書き込み可能フラグ
    318         */
    319        @Override
    320        public boolean canWrite() {
    321                return exists();
    322        }
    323
    324        /**
    325         * 読み取り可能フラグ
    326         *
    327         * ※クラウドストレージの場合は、
    328         * 存在すればtrueを返します。
    329         *
    330         * @return 読み取り可能フラグ
    331         */
    332        @Override
    333        public boolean canRead() {
    334                return exists();
    335        }
    336
    337        /**
    338         * 隠しファイルフラグ
    339         *
    340         * ※クラウドストレージの場合は、
    341         * 必ずfalseを返します。
    342         *
    343         * @return 隠しファイルフラグ
    344         */
    345        @Override
    346        public boolean isHidden() {
    347                return false;
    348        }
    349
    350        /**
    351         * 新規ファイル作成
    352         *
    353         * 既にファイルが存在しない場合のみ、
    354         * 空のファイルを作成します。
    355         *
    356         * @return 成否フラグ
    357         * @throws IOException ファイル関連エラー情報
    358         */
    359        @Override
    360        public boolean createNewFile() throws IOException {
    361                boolean rtn = false;
    362
    363                if (!exists()) {
    364                        InputStream is = null;
    365                        try {
    366                                is = new ByteArrayInputStream(new byte[0]);
    367                                write(is);
    368                                rtn = true;
    369                        } finally {
    370                                Closer.ioClose(is);
    371                        }
    372                }
    373
    374                return rtn;
    375        }
    376
    377        /**
    378         * 最終更新時刻の更新
    379         *
    380         * 最終更新時刻の更新を行います。
    381         * ※クラウドストレージの場合は、
    382         * 最終更新時刻の更新を行えません。
    383         *
    384         * @param time 更新する最終更新時刻
    385         * @return 成否フラグ
    386         */
    387        @Override
    388        public boolean setLastModified(final long time) {
    389                // クラウドストレージでは、setLastModifiedによる、
    390                // 最終更新時刻の設定はできないので、
    391                // 処理を行わずにtrueを返します。
    392                return true;
    393        }
    394
    395        /**
    396         * カノニカルファイル情報の取得
    397         *
    398         * ※ローカルサーバのみ通常ファイルと、
    399         * カノニカルファイルで異なります。
    400         *
    401         * @return カノニカルファイル情報
    402         * @throws IOException ファイル関連エラー情報
    403         */
    404        @Override
    405        public FileOperation getCanonicalFile() throws IOException {
    406                return this;
    407        }
    408
    409        /**
    410         * toString
    411         *
    412         * パスを返します。
    413         *
    414         * @return ファイルパス
    415         */
    416        @Override
    417        public String toString() {
    418                return conPath;
    419        }
    420
    421        /** 共通関数 **/
    422        /**
    423         * ファイルパスの編集
    424         *
    425         * パスの先頭が「/」の場合は「/」の除去と、「//」を「/」に置換処理の追加。
    426         *
    427         * @og.rev 5.10.12.2 (2019/06/17) 相対パス対応
    428         *
    429         * @param path ファイルパス
    430         * @return 変更後パス
    431         */
    432        protected String editPath(final String path) {
    433                if (StringUtil.isNull(path)) {
    434                        return "";
    435                }
    436                String rtn = path;
    437
    438                // 「//+」は「/」に置換
    439                rtn = rtn.replaceAll("//+", "/");
    440                // 先頭が「/」の場合は除去
    441//              if ("/".equals(rtn.substring(0, 1))) {
    442                if( FS == rtn.charAt(0) ) {
    443                        rtn = rtn.substring(1);
    444                }
    445                // 後尾の「.」は除去
    446                rtn = rTrim(rtn, '.');
    447                // 後尾の「/」は除去
    448                rtn = rTrim(rtn, FS);
    449
    450                // 5.10.12.2 (2019/06/17)
    451                // 「../」の文字列は１つ上のディレクトリに変換を行います。
    452                Matcher mtc = PTN_PRE_DIR.matcher(rtn);
    453
    454                // 「../」が無くなるまで、１つずづ変換します。
    455                while(mtc.find()) {
    456                        rtn = mtc.replaceFirst("");
    457                        mtc = PTN_PRE_DIR.matcher(rtn);
    458                }
    459
    460                return rtn;
    461        }
    462
    463        /**
    464         * 親のパスを抽出
    465         *
    466         * キーから親のパスを抽出します。
    467         *
    468         * @param key キー
    469         * @return 親のパス
    470         */
    471        protected String drawParent(final String key) {
    472                final int k = key.lastIndexOf(FS);
    473
    474                String rtn = "";
    475                if (k > 0) {
    476                        rtn = key.substring(0, key.lastIndexOf(FS));
    477                }
    478                if ("/".equals(File.separator)) {
    479                        rtn = File.separator + rtn;
    480                }
    481
    482                return rtn;
    483        }
    484
    485        /**
    486         * 名称の抽出
    487         *
    488         * 引数のkeyから名称を抽出します。
    489         *
    490         * @param key キー(パス)
    491         * @return 名称
    492         */
    493        protected String drawName(final String key) {
    494                final int k = key.lastIndexOf(FS);
    495
    496                String rtn = key;
    497                if (k > 0) {
    498                        rtn = key.substring(key.lastIndexOf(FS) + 1);
    499                }
    500                return rtn;
    501        }
    502
    503        /**
    504         * ディレクトリ用のパス編集
    505         *
    506         * 後尾に「/」がない場合は、付与します。
    507         *
    508         * @param path パス
    509         * @return 後尾に「/」ありのパス
    510         */
    511        protected String setDirTail(final String path) {
    512                if (StringUtil.isNull(path)) {
    513                        return path;
    514                }
    515
    516                final StringBuilder sb = new StringBuilder(path);
    517//              if (!"/".equals(path.substring(path.length() - 1))) {
    518                if ( FS != path.charAt(path.length() - 1) ) {
    519                        sb.append(FS);
    520                }
    521                return sb.toString();
    522        }
    523
    524        /**
    525         * 右側トリム処理
    526         *
    527         * 右側の文字が、指定の文字の場合、除去します。
    528         *
    529         * @param str 対象文字列
    530         * @param chr 指定文字
    531         * @return 右側から指定文字を除去後の文字列
    532         */
    533        protected String rTrim(final String str, final char chr) {
    534                String rtn = str;
    535                int trgPos = 0;
    536                for( int i = str.length() - 1; i >= 0; i--) {
    537                        if (str.charAt(i) == chr) {
    538                                trgPos = i;
    539                                // すべて合致した場合は、から文字を返す
    540                                if (trgPos == 0) {
    541                                        rtn = "";
    542                                }
    543                        } else {
    544                                break;
    545                        }
    546                }
    547
    548                if (trgPos > 0) {
    549                        rtn = str.substring(0, trgPos);
    550                }
    551
    552                return rtn;
    553        }
    554
    555        /**
    556         * ファイル区切り文字変換
    557         *
    558         * ファイル区切り文字を変換します。
    559         *
    560         * @param path 変換前文字列
    561         * @return 返還後文字列
    562         */
    563        protected String replaceFileSeparetor(final String path) {
    564                if (StringUtil.isNull(path)) {
    565                        return "";
    566                }
    567
    568                return path.replaceAll("\\\\", "/");
    569        }
    570
    571        /**
    572         * フィルター処理
    573         *
    574         * フィルター処理を行います。
    575         *
    576         * @param list フィルタを行うリスト
    577         * @param filter フィルタ情報
    578         * @return フィルタ後のリスト
    579         */
    580        protected File[] filter(final List<File> list, final FileFilter filter) {
    581                final List<File> files = new ArrayList<File>();
    582                for( final File file : list ) {
    583                        if (filter.accept(file)) {
    584                                files.add(file);
    585                        }
    586                }
    587                return files.toArray(new File[files.size()]);
    588        }
    589
    590        /**
    591         * ストリームの変換処理
    592         *
    593         * InputStreamをbyte[]に変換。
    594         * InputStreamのサイズ計算に利用。
    595         *
    596         * @param is byte配列変換するInputStream
    597         * @return InpusStreamをbyte配列に変換した値
    598         * @throws IOException ファイル関連エラー情報
    599         */
    600        protected byte[] toByteArray(final InputStream is) throws IOException {
    601                final ByteArrayOutputStream output = new ByteArrayOutputStream();
    602                try {
    603                        // 7.2.9.4 (2020/11/20) Avoid variables with short names like b   b → bt , n → no
    604                        final byte[] bt = new byte[BUFFER_SIZE];
    605                        int no = 0;
    606                        while ((no = is.read(bt)) != -1) {
    607                                output.write(bt, 0, no);
    608                        }
    609                        return output.toByteArray();
    610                } finally {
    611                        output.close();
    612                }
    613        }
    614
    615        /**
    616         * ローカル実行フラグ判定
    617         *
    618         * このabstract クラスの継承クラスはクラウド上で実行されるため、
    619         * falseを返します。
    620         *
    621         * @return ローカル実行フラグ
    622         */
    623        @Override
    624        public boolean isLocal() {
    625                return false;
    626        }
    627
    628        /** java.io.Fileに実装されており、クラウド用ファイルクラスに未実装のメソッドの対応 */
    629        /**
    630         * canExecuteの実行
    631         *
    632         * クラウド側では未実装のメソッドです。
    633         *
    634         * @return フラグ
    635         */
    636        @Override
    637        public boolean canExecute() {
    638                throw new RuntimeException(UNIMPLEMNTED_ERR);
    639        }
    640
    641        /**
    642         * deleteOnExitの実行
    643         *
    644         * クラウド側では未実装のメソッドです。
    645         *
    646         */
    647        @Override
    648        public void deleteOnExit() {
    649                throw new RuntimeException(UNIMPLEMNTED_ERR);
    650        }
    651
    652        /**
    653         * getAbsoluteFileの実行
    654         *
    655         * クラウド側では未実装のメソッドです。
    656         *
    657         * @return Fileｵﾌﾞｼﾞｪｸﾄ
    658         */
    659        @Override
    660        public File getAbsoluteFile() {
    661                throw new RuntimeException(UNIMPLEMNTED_ERR);
    662        }
    663
    664        /**
    665         * getFreeSpaceの実行
    666         *
    667         * クラウド側では未実装のメソッドです。
    668         *
    669         * @return 数値
    670         */
    671        @Override
    672        public long getFreeSpace() {
    673                throw new RuntimeException(UNIMPLEMNTED_ERR);
    674        }
    675
    676        /**
    677         * getTotalSpaceの実行
    678         *
    679         * クラウド側では未実装のメソッドです。
    680         *
    681         * @return 数値
    682         */
    683        @Override
    684        public long getTotalSpace() {
    685                throw new RuntimeException(UNIMPLEMNTED_ERR);
    686        }
    687
    688        /**
    689         * getUsableSpaceの実行
    690         *
    691         * クラウド側では未実装のメソッドです。
    692         *
    693         * @return 数値
    694         */
    695        @Override
    696        public long getUsableSpace() {
    697                throw new RuntimeException(UNIMPLEMNTED_ERR);
    698        }
    699
    700        /**
    701         * isAbsoluteの実行
    702         *
    703         * クラウド側では未実装のメソッドです。
    704         *
    705         * @return フラグ
    706         */
    707        @Override
    708        public boolean isAbsolute() {
    709                throw new RuntimeException(UNIMPLEMNTED_ERR);
    710        }
    711
    712        /**
    713         * setReadableの実行
    714         *
    715         * クラウド側では未実装のメソッドです。
    716         *
    717         * @param readable フラグ
    718         * @return フラグ
    719         */
    720        @Override
    721        public boolean setReadable(final boolean readable) {
    722                throw new RuntimeException(UNIMPLEMNTED_ERR);
    723        }
    724
    725        /**
    726         * setReadableの実行
    727         *
    728         * クラウド側では未実装のメソッドです。
    729         *
    730         * @param readable フラグ
    731         * @param ownerOnly フラグ
    732         * @return フラグ
    733         */
    734        @Override
    735        public boolean setReadable(final boolean readable, final boolean ownerOnly) {
    736                throw new RuntimeException(UNIMPLEMNTED_ERR);
    737        }
    738
    739        /**
    740         * setWritableの実行
    741         *
    742         * クラウド側では未実装のメソッドです。
    743         *
    744         * @param writable フラグ
    745         * @return フラグ
    746         */
    747        @Override
    748        public boolean setWritable(final boolean writable) {
    749                throw new RuntimeException(UNIMPLEMNTED_ERR);
    750        }
    751
    752        /**
    753         * canExecuteの実行
    754         *
    755         * クラウド側では未実装のメソッドです。
    756         *
    757         * @param writable フラグ
    758         * @param ownerOnly フラグ
    759         * @return フラグ
    760         */
    761        @Override
    762        public boolean setWritable(final boolean writable, final boolean ownerOnly) {
    763                throw new RuntimeException(UNIMPLEMNTED_ERR);
    764        }
    765
    766        /**
    767         * canExecuteの実行
    768         *
    769         * クラウド側では未実装のメソッドです。
    770         *
    771         * @return URI情報
    772         */
    773        @Override
    774        public URI toURI() {
    775                throw new RuntimeException(UNIMPLEMNTED_ERR);
    776        }
    777}