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