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