    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.cloud;
    017
    018import java.io.ByteArrayInputStream;
    019import java.io.File;
    020import java.io.FileFilter;
    021import java.io.FileNotFoundException;
    022import java.io.IOException;
    023import java.io.InputStream;
    024import java.util.ArrayList;
    025import java.util.List;
    026
    027import org.opengion.fukurou.model.FileOperation;
    028import org.opengion.fukurou.system.Closer;                                                      // 8.0.0.0 (2021/09/30) util.Closer → system.Closer
    029import org.opengion.fukurou.util.StringUtil;
    030import org.opengion.hayabusa.common.HybsSystem;
    031import org.opengion.hayabusa.common.HybsSystemException;
    032
    033import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // HybsSystem.BUFFER_MIDDLE は fukurou に移動
    034import static org.opengion.fukurou.system.HybsConst.CR ;                        // 8.0.0.1 (2021/10/08)
    035
    036import com.amazonaws.auth.InstanceProfileCredentialsProvider;
    037import com.amazonaws.services.s3.AmazonS3;
    038import com.amazonaws.services.s3.AmazonS3ClientBuilder;
    039import com.amazonaws.services.s3.model.AmazonS3Exception;
    040import com.amazonaws.services.s3.model.ListObjectsV2Request;
    041import com.amazonaws.services.s3.model.ListObjectsV2Result;
    042import com.amazonaws.services.s3.model.ObjectListing;
    043import com.amazonaws.services.s3.model.ObjectMetadata;
    044import com.amazonaws.services.s3.model.PutObjectRequest;
    045import com.amazonaws.services.s3.model.S3Object;
    046import com.amazonaws.services.s3.model.S3ObjectSummary;
    047
    048/**
    049 * FileOperation_AWSは､S3ｽﾄﾚｰｼﾞに対して､
    050 * ﾌｧｲﾙ操作を行うｸﾗｽです｡
    051 *
    052 * 認証は下記の２通りが可能です｡→ １） の方法のみｻﾎﾟｰﾄします｡
    053 *  １）実行ｻｰﾊﾞのEC2のｲﾝｽﾀﾝｽに､S3ｽﾄﾚｰｼﾞのｱｸｾｽ許可を付与する
    054 * <del> ２）ｼｽﾃﾑﾘｿｰｽにｱｸｾｽｷｰ･ｼｰｸﾚｯﾄｷｰ･ｴﾝﾄﾞﾎﾟｲﾝﾄ･ﾚｷﾞｵﾝを登録する
    055 * （CLOUD_STORAGE_S3_ACCESS_KEY､CLOUD_STORAGE_S3_SECRET_KEY､CLOUD_STORAGE_S3_SERVICE_END_POINT､CLOUD_STORAGE_S3_REGION）
    056 * </del>
    057 *
    058 * 注意：
    059 * ﾊﾞｹｯﾄ名は全ﾕｰｻﾞで共有のため､自身のﾊﾞｹｯﾄ名か､作成されていないﾊﾞｹｯﾄ名を指定する必要があります｡
    060 *
    061 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
    062 *
    063 * @version 5
    064 * @author  oota
    065 * @since   JDK7.0
    066 */
    067public class FileOperation_AWS extends CloudFileOperation {
    068        private static final long serialVersionUID = 5108020190201L ;
    069
    070        private static final String PLUGIN = "AWS";                     // 8.0.0.1 (2021/10/08) staticと大文字化
    071
    072        /** ｸﾗｽ変数 */
    073        private final AmazonS3 amazonS3;
    074
    075        /**
    076         * ｺﾝｽﾄﾗｸﾀｰ
    077         *
    078         * 初期化処理です｡
    079         * AWSの認証処理を行います｡
    080         *
    081         * @og.rev 8.0.0.1 (2021/10/08) CLOUD_STORAGE_S3… 関連の方法廃止
    082         *
    083         * @param bucket ﾊﾞｹｯﾄ
    084         * @param inPath ﾊﾟｽ
    085         */
    086        public FileOperation_AWS(final String bucket, final String inPath) {
    087                super(StringUtil.nval(bucket, HybsSystem.sys("CLOUD_BUCKET")), inPath);
    088
    089                // IAMﾛｰﾙによる認証
    090                amazonS3 = AmazonS3ClientBuilder.standard()
    091                                .withCredentials(new InstanceProfileCredentialsProvider(false))
    092                                .build();
    093
    094                try {
    095                        // S3に指定されたﾊﾞｹｯﾄ(ｺﾝﾃﾅ)が存在しない場合は､作成する
    096                        if (!amazonS3.doesBucketExistV2(conBucket)) { // doesBucketExistV2最新JARだと出ている
    097                                amazonS3.createBucket(conBucket);
    098                        }
    099                } catch (final AmazonS3Exception ase) {
    100                        final String errMsg = new StringBuilder(BUFFER_MIDDLE)
    101                                                        .append("IAMﾛｰﾙによる認証が失敗しました｡").append( CR )
    102                                                        .append( inPath ).toString();
    103
    104                        throw new HybsSystemException(errMsg,ase);
    105                }
    106        }
    107
    108        /**
    109         * 書き込み処理(評価用)
    110         *
    111         * Fileを書き込みます｡
    112         *
    113         * @og.rev 8.0.0.1 (2021/10/08) 新規追加
    114         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    115         *
    116         * @param inFile 書き込みFile
    117         * @throws IOException ﾌｧｲﾙ関連ｴﾗｰ情報
    118         */
    119        @Override
    120        public void write(final File inFile) throws IOException {
    121                try {
    122                        amazonS3.putObject(conBucket, conPath, inFile);
    123//              } catch (final Exception ex) {
    124                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    125                        final String errMsg = new StringBuilder(BUFFER_MIDDLE)
    126                                                .append("AWSﾊﾞｹｯﾄに(File)書き込みが失敗しました｡").append( CR )
    127                                                .append(conPath).toString();
    128                        throw new IOException(errMsg,th);
    129                }
    130        }
    131
    132        /**
    133         * 書き込み処理
    134         *
    135         * InputStreamのﾃﾞｰﾀを書き込みます｡
    136         *
    137         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    138         *
    139         * @param is 書き込みﾃﾞｰﾀのInputStream
    140         * @throws IOException ﾌｧｲﾙ関連ｴﾗｰ情報
    141         */
    142        @Override
    143        public void write(final InputStream is) throws IOException {
    144                ByteArrayInputStream bais = null;
    145                try {
    146                        final ObjectMetadata om = new ObjectMetadata();
    147
    148                        final byte[] bytes = toByteArray(is);
    149                        om.setContentLength(bytes.length);
    150                        bais = new ByteArrayInputStream(bytes);
    151
    152                        final PutObjectRequest request = new PutObjectRequest(conBucket, conPath, bais, om);
    153
    154                        amazonS3.putObject(request);
    155//              } catch (final Exception ex) {
    156                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    157                        final String errMsg = new StringBuilder(BUFFER_MIDDLE)
    158                                                .append("AWSﾊﾞｹｯﾄに(InputStream)書き込みが失敗しました｡").append( CR )
    159                                                .append(conPath).toString();
    160                        throw new IOException(errMsg,th);
    161                } finally {
    162                        Closer.ioClose(bais);
    163                }
    164        }
    165
    166        /**
    167         * 読み込み処理
    168         *
    169         * ﾃﾞｰﾀを読み込み､InputStreamとして､返します｡
    170         *
    171         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    172         *
    173         * @return 読み込みﾃﾞｰﾀのInputStream
    174         * @throws FileNotFoundException ﾌｧｲﾙ非存在ｴﾗｰ情報
    175         */
    176        @Override
    177        public InputStream read() throws FileNotFoundException {
    178                S3Object object = null;
    179
    180                try {
    181                        object = amazonS3.getObject(conBucket, conPath);
    182//              } catch (final Exception ex) {
    183                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    184                        final String errMsg = new StringBuilder(BUFFER_MIDDLE)
    185                                                .append("AWSﾊﾞｹｯﾄから読み込みが失敗しました｡").append( CR )
    186                                                .append(conPath).append( CR )
    187                                                .append( th.getMessage() ).toString();
    188                        throw new FileNotFoundException(errMsg);                                // FileNotFoundException は、Throwable 引数を持つコンストラクタはなない。
    189                }
    190                return object.getObjectContent();       // com.amazonaws.services.s3.model.S3ObjectInputStream
    191        }
    192
    193        /**
    194         * 削除処理
    195         *
    196         * ﾌｧｲﾙを削除します｡
    197         *
    198         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    199         *
    200         * @return 成否ﾌﾗｸﾞ
    201         */
    202        @Override
    203        public boolean delete() {
    204                boolean flgRtn = false;
    205
    206                try {
    207                        if (isFile()) {
    208                                // ﾌｧｲﾙ削除
    209                                amazonS3.deleteObject(conBucket, conPath);
    210                        } else if (isDirectory()) {
    211                                // ﾃﾞｨﾚｸﾄﾘ削除
    212                                // 一括削除のapiが無いので､繰り返しで削除を行う
    213                                final ObjectListing objectList = amazonS3.listObjects(conBucket, conPath);
    214                                final List<S3ObjectSummary> list = objectList.getObjectSummaries();
    215                                for (final S3ObjectSummary obj : list) {
    216                                        amazonS3.deleteObject(conBucket, obj.getKey());
    217                                }
    218                        }
    219                        flgRtn = true;
    220//              } catch (final Exception ex) {
    221                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    222                        // ｴﾗｰはｽﾙｰして､falseを返す
    223                        System.out.println( th.getMessage() );
    224                }
    225
    226                return flgRtn;
    227        }
    228
    229        /**
    230         * ｺﾋﾟｰ処理
    231         *
    232         * ﾌｧｲﾙを指定先に､ｺﾋﾟｰします｡
    233         *
    234         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    235         *
    236         * @param afPath ｺﾋﾟｰ先
    237         * @return 成否ﾌﾗｸﾞ
    238         */
    239        @Override
    240        public boolean copy(final String afPath) {
    241                boolean flgRtn = false;
    242
    243                try {
    244                        amazonS3.copyObject(conBucket, conPath, conBucket, afPath);
    245                        flgRtn = true;
    246//              } catch (final Exception ex) {
    247                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    248                        // ｴﾗｰはｽﾙｰして､falseを返す
    249                        System.out.println( th.getMessage() );
    250                }
    251
    252                return flgRtn;
    253        }
    254
    255        /**
    256         * ﾌｧｲﾙｻｲｽﾞ取得
    257         *
    258         * ﾌｧｲﾙｻｲｽﾞを返します｡
    259         *
    260         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    261         *
    262         * @return ﾌｧｲﾙｻｲｽﾞ
    263         */
    264        @Override
    265        public long length() {
    266                long rtn = 0;
    267
    268                try {
    269                        final ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath);
    270                        rtn = meta.getContentLength();
    271//              } catch (final Exception ex) {
    272                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    273                        // ｴﾗｰはｽﾙｰして､0を返す｡
    274                        System.out.println( th.getMessage() );
    275                }
    276                return rtn;
    277        }
    278
    279        /**
    280         * 最終更新時刻取得
    281         *
    282         * 最終更新時刻を取得します｡
    283         *
    284         * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。
    285         *
    286         * @return 最終更新時刻
    287         */
    288        @Override
    289        public long lastModified() {
    290                long rtn = 0;
    291
    292                try {
    293                        final ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath);
    294                        rtn = meta.getLastModified().getTime();
    295//              } catch (final Exception ex) {
    296                } catch( final Throwable th ) {         // 8.0.2.0 (2021/11/30)
    297                        // ｴﾗｰはｽﾙｰして､0を返す
    298                        System.out.println( th.getMessage() );
    299                }
    300                return rtn;
    301        }
    302
    303        /**
    304         * ﾌｧｲﾙ判定
    305         *
    306         * ﾌｧｲﾙの場合は､trueを返します｡
    307         *
    308         * @return ﾌｧｲﾙ判定ﾌﾗｸﾞ
    309         */
    310        @Override
    311        public boolean isFile() {
    312                boolean rtn = false;
    313
    314                if(!isDirectory()) {
    315                        rtn = amazonS3.doesObjectExist(conBucket, conPath);
    316                }
    317
    318                return rtn;
    319        }
    320
    321        /**
    322         * ﾃﾞｨﾚｸﾄﾘ判定
    323         *
    324         * ﾃﾞｨﾚｸﾄﾘの場合は､trueを返します｡
    325         *
    326         * @return ﾃﾞｨﾚｸﾄﾘ判定ﾌﾗｸﾞ
    327         */
    328        @Override
    329        public boolean isDirectory() {
    330                if (StringUtil.isEmpty(conPath)) {              // 8.0.1.0 (2021/10/29) org.apache.commons.lang3.StringUtils 置換
    331                        return true;
    332                }
    333
    334                // S3にはﾃﾞｨﾚｸﾄﾘの概念はないので､｢/｣で続くﾃﾞｰﾀが存在するかで､判定
    335                final ObjectListing objectList = amazonS3.listObjects(conBucket, setDirTail(conPath));
    336                final List<S3ObjectSummary> list = objectList.getObjectSummaries();
    337
    338//              return list.size() != 0 ;
    339                return ! list.isEmpty() ;
    340        }
    341
    342        /**
    343         * ﾌｧｲﾙ一覧取得
    344         *
    345         * ﾊﾟｽのﾌｧｲﾙとﾃﾞｨﾚｸﾄﾘ一覧を取得します｡
    346         *
    347         * @og.rev 8.0.2.0 (2021/11/30) fukurou.util.rTrim(String,char) 使用
    348         *
    349         * @param filter ﾌｨﾙﾀ情報
    350         * @return ﾌｧｲﾙとﾃｨﾚｸﾄﾘ一覧
    351         */
    352        @Override
    353        public File[] listFiles(final FileFilter filter) {
    354                if (!exists()) {
    355                        return new FileOperationInfo[0];
    356                }
    357
    358                String search = conPath;
    359                if (isDirectory()) {
    360                        search = setDirTail(conPath);
    361                }
    362
    363                final List<File> rtnList = new ArrayList<File>();
    364
    365                // 検索処理
    366                final ListObjectsV2Request request = new ListObjectsV2Request()
    367                                .withBucketName(conBucket)
    368                                .withPrefix(search)
    369                                .withDelimiter("/");
    370                final ListObjectsV2Result list = amazonS3.listObjectsV2(request);
    371                final List<S3ObjectSummary> objects = list.getObjectSummaries();
    372
    373                // ﾌｧｲﾙ情報の取得
    374                for (final S3ObjectSummary obj : objects) {
    375                        final String key = obj.getKey();
    376
    377                        final FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key);
    378                        file.setLastModifiedValue(obj.getLastModified().getTime());
    379                        file.setFile(true);
    380                        file.setSize(obj.getSize());
    381                        rtnList.add(file);
    382                }
    383
    384                // ﾃﾞｨﾚｸﾄﾘ情報の取得
    385                final List<String> folders = list.getCommonPrefixes();
    386                for (final String str : folders) {
    387//                      final String key = rTrim(str, '/');
    388                        final String key = StringUtil.rTrim(str, '/');                  // 8.0.2.0 (2021/11/30)
    389
    390                        final FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key);
    391                        file.setDirectory(true);
    392                        rtnList.add(file);
    393                }
    394
    395                // ﾌｨﾙﾀ処理
    396                return filter(rtnList, filter);
    397        }
    398
    399        /**
    400         * 親ﾃﾞｨﾚｸﾄﾘ情報の取得
    401         *
    402         * 親のﾃﾞｨﾚｸﾄﾘを返します｡
    403         *
    404         * @return 親のﾃﾞｨﾚｸﾄﾘ情報
    405         */
    406        @Override
    407        public FileOperation getParentFile() {
    408                return new FileOperation_AWS(conBucket, this.getParent());
    409        }
    410}