    001package org.opengion.plugin.cloud;
    002
    003import java.io.ByteArrayInputStream;
    004import java.io.File;
    005import java.io.FileFilter;
    006import java.io.FileNotFoundException;
    007import java.io.IOException;
    008import java.io.InputStream;
    009import java.util.ArrayList;
    010import java.util.List;
    011
    012import org.apache.commons.lang3.StringUtils;
    013import org.opengion.fukurou.model.CloudFileOperation;
    014import org.opengion.fukurou.model.FileOperation;
    015import org.opengion.fukurou.model.FileOperationInfo;
    016import org.opengion.fukurou.system.Closer;                                              // 8.0.0.0 (2021/09/30) util.Closer → system.Closer
    017import org.opengion.fukurou.util.StringUtil;
    018import org.opengion.hayabusa.common.HybsSystem;
    019import org.opengion.hayabusa.common.HybsSystemException;
    020
    021import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // HybsSystem.BUFFER_MIDDLE は fukurou に移動
    022
    023import com.amazonaws.auth.AWSCredentials;
    024import com.amazonaws.auth.AWSStaticCredentialsProvider;
    025import com.amazonaws.auth.BasicAWSCredentials;
    026import com.amazonaws.auth.InstanceProfileCredentialsProvider;
    027import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
    028import com.amazonaws.services.s3.AmazonS3;
    029import com.amazonaws.services.s3.AmazonS3ClientBuilder;
    030import com.amazonaws.services.s3.model.AmazonS3Exception;
    031import com.amazonaws.services.s3.model.ListObjectsV2Request;
    032import com.amazonaws.services.s3.model.ListObjectsV2Result;
    033import com.amazonaws.services.s3.model.ObjectListing;
    034import com.amazonaws.services.s3.model.ObjectMetadata;
    035import com.amazonaws.services.s3.model.PutObjectRequest;
    036import com.amazonaws.services.s3.model.S3Object;
    037import com.amazonaws.services.s3.model.S3ObjectSummary;
    038
    039/**
    040 * FileOperation_AWSは、S3ストレージに対して、
    041 * ファイル操作を行うクラスです。
    042 *
    043 * 認証は下記の２通りが可能です。
    044 * ・実行サーバのEC2のインスタンスに、S3ストレージのアクセス許可を付与する
    045 * ・システムリソースにアクセスキー・シークレットキー・エンドポイント・レギオンを登録する
    046 * （CLOUD_STORAGE_S3_ACCESS_KEY、CLOUD_STORAGE_S3_SECRET_KEY、CLOUD_STORAGE_S3_SERVICE_END_POINT、CLOUD_STORAGE_S3_REGION）
    047 *
    048 * 注意：
    049 * バケット名は全ユーザで共有のため、自身のバケット名か、作成されていないバケット名を指定する必要があります。
    050 *
    051 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
    052 *
    053 * @version 5
    054 * @author  oota
    055 * @since   JDK7.0
    056 */
    057public class FileOperation_AWS extends CloudFileOperation {
    058        private static final long serialVersionUID = 5108020190201L ;
    059
    060        /** クラス変数 */
    061        private final AmazonS3 amazonS3;
    062        private final String PLUGIN = "aws";
    063
    064        /**
    065         * コンストラクター
    066         *
    067         * 初期化処理です。
    068         * AWSの認証処理を行います。
    069         *
    070         * @param bucket バケット
    071         * @param inPath パス
    072         */
    073        public FileOperation_AWS(final String bucket, final String inPath) {
    074                super(StringUtil.nval(bucket, HybsSystem.sys("CLOUD_BUCKET")), inPath);
    075                setPlugin(PLUGIN);
    076
    077                // ｱｸｾｽｷｰ
    078                final String s3AccessKey = HybsSystem.sys("CLOUD_STORAGE_S3_ACCESS_KEY");
    079                String s3SecretKey = "";
    080                String s3ServiceEndPoint = "";
    081                String s3Region = "";
    082
    083                // S3アクセスクライアントの生成
    084                if (StringUtils.isEmpty(s3AccessKey)) {
    085                        // IAMロールによる認証
    086                        amazonS3 = AmazonS3ClientBuilder.standard()
    087                                        .withCredentials(new InstanceProfileCredentialsProvider(false))
    088                                        .build();
    089                } else {
    090                        // リソースのアクセスキーによる認証
    091                        // ｼｰｸﾚｯﾄｷｰ
    092                        s3SecretKey = HybsSystem.sys("CLOUD_STORAGE_S3_SECRET_KEY");
    093                        // ｴﾝﾄﾞﾎﾟｲﾝﾄ
    094                        s3ServiceEndPoint = HybsSystem.sys("CLOUD_STORAGE_S3_SERVICE_END_POINT");
    095                        // ﾚｷﾞｵﾝ
    096                        s3Region = HybsSystem.sys("CLOUD_STORAGE_S3_REGION");
    097
    098                        // AWSの認証情報
    099                        AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
    100
    101                        // エンドポイント設定
    102                        EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint, s3Region);
    103                        amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials))
    104                                        .withEndpointConfiguration(endpointConfiguration)
    105                                        .build();
    106                }
    107
    108                try {
    109                        // S3に指定されたﾊﾞｹｯﾄ(ｺﾝﾃﾅ)が存在しない場合は、作成する
    110                        if (!amazonS3.doesBucketExistV2(conBucket)) { // doesBucketExistV2最新JARだと出ている
    111                                amazonS3.createBucket(conBucket);
    112                        }
    113                } catch (AmazonS3Exception ase) {
    114                        StringBuilder errMsg = new StringBuilder(BUFFER_MIDDLE);
    115                        if (StringUtils.isEmpty(s3AccessKey)) {
    116                                errMsg.append("IAMロールによる認証が失敗しました。");
    117
    118                        } else {
    119                                errMsg.append("アクセスキーによる認証が失敗しました。");
    120                                errMsg.append(" CLOUD_STORAGE_S3_ACCESS_KEY：").append(s3AccessKey);
    121                                errMsg.append(" CLOUD_STORAGE_S3_SECRET_KEY：非表示");
    122                                errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT：").append(s3ServiceEndPoint);
    123                                errMsg.append(" CLOUD_STORAGE_S3_REGION：").append(s3Region);
    124                        }
    125                        errMsg.append(" システムエラー情報：").append(ase.getMessage());
    126                        throw new HybsSystemException(errMsg.toString());
    127                }
    128        }
    129
    130        /**
    131         * 書き込み処理
    132         *
    133         * InputStreamのデータを書き込みます。
    134         *
    135         * @param is 書き込みデータのInputStream
    136         * @throws IOException ファイル関連エラー情報
    137         */
    138        @Override
    139        public void write(final InputStream is) throws IOException {
    140                ByteArrayInputStream bais = null;
    141                try {
    142                        ObjectMetadata om = new ObjectMetadata();
    143
    144                        byte[] bytes = toByteArray(is);
    145                        om.setContentLength(bytes.length);
    146                        bais = new ByteArrayInputStream(bytes);
    147
    148                        PutObjectRequest request = new PutObjectRequest(conBucket, conPath, bais, om);
    149
    150                        amazonS3.putObject(request);
    151                } catch (Exception e) {
    152                        StringBuilder errMsg = new StringBuilder(BUFFER_MIDDLE);
    153                        errMsg.append("AWSバケットに書き込みが失敗しました。path：").append(conPath);
    154                        errMsg.append(" システムエラー情報：").append(e.getMessage());
    155                        throw new IOException(errMsg.toString());
    156                } finally {
    157                        Closer.ioClose(bais);
    158                }
    159        }
    160
    161        /**
    162         * 読み込み処理
    163         *
    164         * データを読み込み、InputStreamとして、返します。
    165         *
    166         * @return 読み込みデータのInputStream
    167         * @throws FileNotFoundException ファイル非存在エラー情報
    168         */
    169        @Override
    170        public InputStream read() throws FileNotFoundException {
    171                S3Object object = null;
    172
    173                try {
    174                        object = amazonS3.getObject(conBucket, conPath);
    175                } catch (Exception e) {
    176                        StringBuilder errMsg = new StringBuilder(BUFFER_MIDDLE);
    177                        errMsg.append("AWSバケットから読み込みが失敗しました。path：").append(conPath);
    178                        errMsg.append(" システムエラー情報：").append(e.getMessage());
    179                        throw new FileNotFoundException(errMsg.toString());
    180                }
    181                return object.getObjectContent();
    182        }
    183
    184        /**
    185         * 削除処理
    186         *
    187         * ファイルを削除します。
    188         *
    189         * @return 成否フラグ
    190         */
    191        @Override
    192        public boolean delete() {
    193                boolean flgRtn = false;
    194
    195                try {
    196                        if (isFile()) {
    197                                // ファイル削除
    198                                amazonS3.deleteObject(conBucket, conPath);
    199                        } else if (isDirectory()) {
    200                                // ディレクトリ削除
    201                                // 一括削除のapiが無いので、繰り返しで削除を行う
    202                                ObjectListing objectList = amazonS3.listObjects(conBucket, conPath);
    203                                List<S3ObjectSummary> list = objectList.getObjectSummaries();
    204                                for (S3ObjectSummary obj : list) {
    205                                        amazonS3.deleteObject(conBucket, obj.getKey());
    206                                }
    207
    208                        }
    209                        flgRtn = true;
    210                } catch (Exception e) {
    211                        // エラーはスルーして、falseを返す
    212                }
    213
    214                return flgRtn;
    215        }
    216
    217        /**
    218         * コピー処理
    219         *
    220         * ファイルを指定先に、コピーします。
    221         *
    222         * @param afPath コピー先
    223         * @return 成否フラグ
    224         */
    225        @Override
    226        public boolean copy(final String afPath) {
    227                boolean flgRtn = false;
    228
    229                try {
    230                        amazonS3.copyObject(conBucket, conPath, conBucket, afPath);
    231                        flgRtn = true;
    232                } catch (Exception e) {
    233                        // エラーはスルーして、falseを返す
    234                }
    235
    236                return flgRtn;
    237        }
    238
    239        /**
    240         * ファイルサイズ取得
    241         *
    242         * ファイルサイズを返します。
    243         *
    244         * @return ファイルサイズ
    245         */
    246        @Override
    247        public long length() {
    248                long rtn = 0;
    249
    250                try {
    251                        ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath);
    252                        rtn = meta.getContentLength();
    253                } catch (Exception e) {
    254                        // エラーはスルーして、0を返す。
    255                }
    256                return rtn;
    257        }
    258
    259        /**
    260         * 最終更新時刻取得
    261         *
    262         * 最終更新時刻を取得します。
    263         *
    264         * @return 最終更新時刻
    265         */
    266        @Override
    267        public long lastModified() {
    268                long rtn = 0;
    269
    270                try {
    271                        ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath);
    272                        rtn = meta.getLastModified().getTime();
    273                } catch (Exception e) {
    274                        // エラーはスルーして、0を返す
    275                }
    276                return rtn;
    277        }
    278
    279        /**
    280         * ファイル判定
    281         *
    282         * ファイルの場合は、trueを返します。
    283         *
    284         * @return ファイル判定フラグ
    285         */
    286        @Override
    287        public boolean isFile() {
    288                boolean rtn = false;
    289
    290                if(!isDirectory()) {
    291                        rtn = amazonS3.doesObjectExist(conBucket, conPath);
    292                }
    293
    294                return rtn;
    295        }
    296
    297        /**
    298         * ディレクトリ判定
    299         *
    300         * ディレクトリの場合は、trueを返します。
    301         *
    302         * @return ディレクトリ判定フラグ
    303         */
    304        @Override
    305        public boolean isDirectory() {
    306                boolean flgRtn = false;
    307
    308                if (StringUtils.isEmpty(conPath)) {
    309                        return true;
    310                }
    311
    312                // S3にはディレクトリの概念はないので、「/」で続くデータが存在するかで、判定
    313                ObjectListing objectList = amazonS3.listObjects(conBucket, setDirTail(conPath));
    314                List<S3ObjectSummary> list = objectList.getObjectSummaries();
    315                flgRtn = list.size() == 0 ? false : true;
    316
    317                return flgRtn;
    318        }
    319
    320        /**
    321         * ファイル一覧取得
    322         *
    323         * パスのファイルとディレクトリ一覧を取得します。
    324         *
    325         * @param filter フィルタ情報
    326         * @return ファイルとティレクトリ一覧
    327         */
    328        @Override
    329        public File[] listFiles(final FileFilter filter) {
    330                if (!exists()) {
    331                        return new FileOperationInfo[0];
    332                }
    333
    334                String search = conPath;
    335                if (isDirectory()) {
    336                        search = setDirTail(conPath);
    337                }
    338
    339                List<File> rtnList = new ArrayList<File>();
    340
    341                // 検索処理
    342                ListObjectsV2Request request = new ListObjectsV2Request()
    343                                .withBucketName(conBucket)
    344                                .withPrefix(search)
    345                                .withDelimiter("/");
    346                ListObjectsV2Result list = amazonS3.listObjectsV2(request);
    347                List<S3ObjectSummary> objects = list.getObjectSummaries();
    348
    349                // ファイル情報の取得
    350                for (S3ObjectSummary obj : objects) {
    351                        String key = obj.getKey();
    352
    353                        FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key);
    354                        file.setLastModifiedValue(obj.getLastModified().getTime());
    355                        file.setFile(true);
    356                        file.setSize(obj.getSize());
    357                        rtnList.add(file);
    358                }
    359
    360                // ディレクトリ情報の取得
    361                List<String> folders = list.getCommonPrefixes();
    362                for (String str : folders) {
    363                        String key = rTrim(str, '/');
    364
    365                        FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key);
    366                        file.setDirectory(true);
    367                        rtnList.add(file);
    368                }
    369
    370                // フィルタ処理
    371                File[] filterList = filter(rtnList, filter);
    372
    373                return filterList;
    374        }
    375
    376        /**
    377         * 親ディレクトリ情報の取得
    378         *
    379         * 親のディレクトリを返します。
    380         *
    381         * @return 親のディレクトリ情報
    382         */
    383        @Override
    384        public FileOperation getParentFile() {
    385                return new FileOperation_AWS(conBucket, this.getParent());
    386        }
    387
    388        /** 以下はローカル環境でのテスト用メソッドです。 */
    389//                              /**
    390//                               * ローカルでのテスト用コンストラクタ
    391//                               *
    392//                               * 要：super.bucketの値は固定値に変更してください。
    393//                               *
    394//                               * @param inPath
    395//                               */
    396//                              public FileOperation_AWS(String bucket, String inPath, boolean test) {
    397//                                      // super( StringUtil.nval( bucket, HybsSystem.sys("CLOUD_BUCKET") ), inPath);
    398//                                      super( StringUtil.nval( bucket, "opengiontestbucket" ), inPath);
    399//
    400//                                      // aws接続情報
    401//                                       String s3AccessKey = "[key]";
    402//                                       String s3SecretKey = "[secret]";
    403//                                      String s3ServiceEndPoint = "s3-ap-northeast-1.amazonaws.com";
    404//                                      String s3Region = "ap-northeast-1";
    405//
    406//                                      // proxy環境での設定
    407//                                      ClientConfiguration conf = new ClientConfiguration();
    408//                                      conf.setProtocol(Protocol.HTTPS);
    409//                                      conf.setProxyHost("mtc-px14");
    410//                                      conf.setProxyPort(8081);
    411//
    412//                                      // AWSの認証情報
    413//                                      AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
    414//
    415//                                      // エンドポイント設定
    416//                                      EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint, s3Region);
    417//                                      amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials))
    418//                                                      .withEndpointConfiguration(endpointConfiguration)
    419//                                                      .withClientConfiguration(conf) // テスト用にproxy設定
    420//                                                      .build();
    421//
    422//                                      try {
    423//                                              // S3に指定されたﾊﾞｹｯﾄ(ｺﾝﾃﾅ)が存在しない場合は、作成する
    424//                                              if (!amazonS3.doesBucketExistV2(conBucket)) {
    425//                                                      amazonS3.createBucket(conBucket);
    426//                                              }
    427//                                      } catch (AmazonS3Exception ase) {
    428//                                              StringBuilder errMsg = new StringBuilder(BUFFER_MIDDLE);
    429//                                              if (StringUtils.isEmpty(s3AccessKey)) {
    430//                                                      errMsg.append("IAMロールによる認証か、バケットの作成が失敗しました。");
    431//
    432//                                              } else {
    433//                                                      errMsg.append("アクセスキーによる認証かバケットの作成が失敗しました。");
    434//                                                      errMsg.append(" CLOUD_STORAGE_S3_ACCESS_KEY：").append(s3AccessKey);
    435//                                                      errMsg.append(" CLOUD_STORAGE_S3_SECRET_KEY：非表示");
    436//                                                      errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT：").append(s3ServiceEndPoint);
    437//                                                      errMsg.append(" CLOUD_STORAGE_S3_REGION：").append(s3Region);
    438//                                              }
    439//                                              errMsg.append(" バケット名：").append(conBucket);
    440//                                              errMsg.append(" システムエラー情報：").append(ase.getMessage());
    441//                                              throw new RuntimeException(errMsg.toString());
    442//                                      }
    443//                              }
    444//
    445//                              /** テスト用メソッド */
    446//                              public static void main(String[] args) {
    447////                                    writeTest();
    448////                                    listTest();
    449////                                    methodTest();
    450//                                      copyTest();
    451//                              }
    452//
    453//                              public static void copyTest() {
    454//                                      File[] list = new File[] {new File("LIST01.xls"), new File("LIST02.ods")};
    455//
    456////                                    System.out.println(file.exists());
    457//
    458//                                      for(File file: list) {
    459//                                              FileOperation file2 = new FileOperation_AWS("ootalocaltest", file.getName(), true);
    460//                                              FileUtil.copy(file,  file2);
    461//                                      }
    462//                              }
    463//
    464//                              public static void writeTest() {
    465//                                      FileOperation file = new FileOperation_AWS("otlocaltest01", "sample/test.txt", true);
    466//                                      try(InputStream is = new ByteArrayInputStream("sample".getBytes())){
    467//                                              file.write(is);
    468//                                      }catch(Exception e) {
    469//                                              System.out.println(e.getMessage());
    470//                                      }
    471//
    472////                                    File file
    473//                              }
    474//
    475//                              public static void listTest() {
    476//                                      File file = new FileOperation_AWS("otlocaltest01", "sample", true);
    477//
    478//                                      // フィルタ設定
    479//                                      HybsFileFilter filter = new HybsFileFilter();
    480//                                      filter.startsWith("te");
    481//
    482//                                      File[] list = file.listFiles(filter);
    483//
    484//                                      System.out.println(list.length);
    485//                                      for(File f: list) {
    486//                                              FileOperation fo = (FileOperation)f;
    487//
    488//                                              try(InputStream is = fo.read()){
    489//                                                      Files.copy(is, Paths.get("out.txt"), StandardCopyOption.REPLACE_EXISTING);
    490//                                              }catch(IOException ioe) {
    491//                                                      System.out.println(ioe);
    492//                                              }
    493//                                      }
    494//                              }
    495//
    496//                              public static void methodTest() {
    497//                                      FileOperation file = new FileOperation_AWS("otlocaltest01", "", true);
    498//                                      boolean rtn = false;
    499////                                    rtn = file.isDirectory();
    500//                                      rtn = file.isFile();
    501//
    502//                                      System.out.println(rtn);
    503//                              }
    504}