    001package org.opengion.plugin.cloud;
    002
    003import java.io.InputStream;
    004import java.text.SimpleDateFormat;
    005import java.util.ArrayList;
    006import java.util.HashMap;
    007import java.util.List;
    008import java.util.Map;
    009
    010// import javax.servlet.http.HttpSession;
    011import jakarta.servlet.http.HttpSession;                                                // 8.0.0.0 (2021/09/30) Tomcat10 対応
    012
    013import org.apache.commons.lang3.StringUtils;
    014import org.opengion.fukurou.system.Closer;                                              // 8.0.0.0 (2021/09/30) util.Closer → system.Closer
    015import org.opengion.fukurou.util.FileInfo;                                              // 8.0.0.0 (2021/09/30) FileUtil → FileInfo
    016import org.opengion.hayabusa.common.HybsSystem;
    017import org.opengion.hayabusa.common.HybsSystemException;
    018import org.opengion.hayabusa.io.StorageAPI;
    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.CopyObjectRequest;
    028import com.amazonaws.services.s3.model.DeleteObjectRequest;
    029import com.amazonaws.services.s3.model.ListObjectsV2Request;
    030import com.amazonaws.services.s3.model.ListObjectsV2Result;
    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// import com.microsoft.azure.storage.blob.BlobOutputStream;
    036
    037/**
    038 * aws用のクラウドストレージ操作実装
    039 *
    040 * システムリソースのS3_ACCESS_KEY,S3_SECRET_KEY,S3_SERVICE_END_POINT,S3_REGIONに、AWSのキー情報を登録する必要があります。
    041 * （IAMを利用する場合には認証情報を登録する必要はありません）
    042 *
    043 * また、Edit機能のファイル出力を利用する場合はS3上(例えばvar/lib/tomcat8/webapps/ge/jsp/common)に
    044 * fileDownloadListDef.txtをアップロードしておく必要があります。
    045 *
    046 * @og.group クラウド
    047 * @og.rev      (2018/02/15) 新規作成
    048 * @og.rev 5.9.32.1 (2018/05/11) パスの先頭が「/」の場合は「/」の除去と、「//」を「/」に置換処理の追加。
    049 *
    050 * @version 5.0
    051 * @author T.OTA
    052 * @since JDK7.0
    053 */
    054public class StorageAPI_aws implements StorageAPI {
    055        // 認証文字列
    056        // ｱｸｾｽｷｰ
    057        private String s3AccessKey = "";
    058        // ｼｰｸﾚｯﾄｷｰ
    059        private String s3SecretKey = "";
    060        // ｴﾝﾄﾞﾎﾟｲﾝﾄ
    061        private String s3ServiceEndPoint = "";
    062        // ﾚｷﾞｵﾝ
    063        private String s3Region = "";
    064        // ﾊﾞｹｯﾄ名(ｺﾝﾃﾅ名)
    065        private String s3bucket = "";                   // 8.0.0.0 (2021/09/30) private化
    066
    067        private AmazonS3 client = null;                 // 8.0.0.0 (2021/09/30) private化
    068
    069        /**
    070         * コンストラクタ
    071         *
    072         * @param container コンテナ
    073         * @param hsession HTTPセッション
    074         */
    075        public StorageAPI_aws(final String container, final HttpSession hsession){
    076                // ﾘｿｰｽﾊﾟﾗﾒｰﾀ設定
    077                // ｱｸｾｽｷｰ
    078                s3AccessKey = HybsSystem.sys("CLOUD_STORAGE_S3_ACCESS_KEY");
    079                // コンテナ名をs3bucketとして保持しておく
    080                s3bucket = container;
    081
    082                // S3アクセスクライアントの生成
    083                if(StringUtils.isEmpty(s3AccessKey)){
    084                        // IAMロールによる認証
    085                        client = AmazonS3ClientBuilder.standard()
    086                                        .withCredentials(new InstanceProfileCredentialsProvider(false))
    087                                        .build();
    088                }else {
    089                        // リソースのアクセスキーによる認証
    090                        // ｼｰｸﾚｯﾄｷｰ
    091                        s3SecretKey = HybsSystem.sys("CLOUD_STORAGE_S3_SECRET_KEY");
    092                        // ｴﾝﾄﾞﾎﾟｲﾝﾄ
    093                        s3ServiceEndPoint = HybsSystem.sys("CLOUD_STORAGE_S3_SERVICE_END_POINT");
    094                        // ﾚｷﾞｵﾝ
    095                        s3Region = HybsSystem.sys("CLOUD_STORAGE_S3_REGION");
    096
    097                        // 初期ﾁｪｯｸ
    098                        initCheck();
    099
    100                        // AWSの認証情報
    101                        AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
    102
    103                        // エンドポイント設定
    104                        EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint,      s3Region);
    105                        client = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials))
    106                                        .withEndpointConfiguration(endpointConfiguration).build();
    107                }
    108
    109                // S3に指定されたﾊﾞｹｯﾄ(ｺﾝﾃﾅ)が存在しない場合は、作成する
    110                if(!client.doesBucketExistV2(container)){
    111                        client.createBucket(container);
    112                }
    113        }
    114
    115        /**
    116         * 初期ﾁｪｯｸ
    117         */
    118        private void initCheck(){
    119                // ｼｽﾃﾑﾘｿｰｽに認証情報が登録されていない場合は、エラー
    120                StringBuilder errString = new StringBuilder();
    121                if(StringUtils.isEmpty(s3AccessKey)){
    122                        errString.append("CLOUD_STORAGE_S3_ACCESS_KEY");
    123                }
    124                if(StringUtils.isEmpty(s3SecretKey)){
    125                        errString.append(",CLOUD_STORAGE_S3_SECRET_KEY");
    126                }
    127                if(StringUtils.isEmpty(s3ServiceEndPoint)){
    128                        errString.append(",CLOUD_STORAGE_S3_SERVICE_END_POINT");
    129                }
    130                if(StringUtils.isEmpty(s3Region)){
    131                        errString.append(",CLOUD_STORAGE_S3_REGION");
    132                }
    133
    134                if(errString.length() > 0){
    135                        throw new HybsSystemException("AWSのｷｰ情報("+errString.toString()+")がｼｽﾃﾑﾘｿｰｽに登録されていません。");
    136                }
    137
    138        }
    139
    140        /**
    141         * ファイルパスの編集 2018/05/07 ADD
    142         * パスの先頭が「/」の場合は「/」の除去と、「//」を「/」に置換処理の追加。
    143         *
    144         * @og.rev 5.9.32.1 (2018/05/11)
    145         * @param path
    146         * @return 変更後パス
    147         */
    148        private String editPath(String path) {
    149                // 先頭が「/」の場合は除去
    150                if("/".equals(path.substring(0, 1))) {
    151                        path = path.substring(1);
    152                }
    153                // 「//」は「/」に置換
    154                path = path.replaceAll("//", "/");
    155
    156                return path;
    157        }
    158
    159        /**
    160         * ｱｯﾌﾟﾛｰﾄﾞ
    161         *
    162         * @og.rev 5.9.32.1 (2018/05/11)
    163         * @param partInputStream       ｱｯﾌﾟﾛｰﾄﾞ対象のｽﾄﾘｰﾑ
    164         * @param updFolder             ｱｯﾌﾟﾛｰﾄﾞﾌｫﾙﾀ名
    165         * @param updFileName           ｱｯﾌﾟﾛｰﾄﾞﾌｧｲﾙ名
    166         * @param hsession                      ｾｯｼｮﾝ
    167         */
    168        @Override
    169        public void add(InputStream partInputStream, String updFolder, String updFileName, HttpSession hsession) {
    170//              BlobOutputStream blobOutputStream = null;
    171                try {
    172                        // 2018/05/07 ADD
    173                        updFolder = editPath(updFolder);
    174
    175                        // ｱｯﾌﾟﾛｰﾄﾞ処理
    176                        ObjectMetadata om = new ObjectMetadata();
    177
    178                        final PutObjectRequest putRequest = new PutObjectRequest(s3bucket, updFolder + updFileName, partInputStream,om);
    179                        // ｱｯﾌﾟﾛｰﾄﾞ実行
    180                        client.putObject(putRequest);
    181
    182                } catch (Exception e) {
    183                        StringBuilder sbErrMsg = new StringBuilder();
    184                        sbErrMsg.append("ストレージへのファイルアップロードに失敗しました。updFolder:");
    185                        sbErrMsg.append(updFolder);
    186                        sbErrMsg.append(" updFileName:");
    187                        sbErrMsg.append(updFileName);
    188                        sbErrMsg.append(" errInfo:");
    189                        sbErrMsg.append(e);
    190                        throw new HybsSystemException(sbErrMsg.toString());
    191                } finally {
    192                        // ｸﾛｰｽﾞ処理
    193//                      Closer.ioClose(blobOutputStream);
    194                        Closer.ioClose(partInputStream);
    195                }
    196        }
    197
    198        /**
    199         * ﾀﾞｳﾝﾛｰﾄﾞ
    200         *
    201         * @og.rev 5.9.32.1 (2018/05/11)
    202         * @param filePath      ﾀﾞｳﾝﾛｰﾄﾞ対象のﾌｧｲﾙﾊﾟｽ
    203         * @param hsession      ｾｯｼｮﾝ
    204         * @return ストリーム
    205         */
    206        @Override
    207        public InputStream get(String filePath, HttpSession hsession) {
    208                InputStream is = null;
    209                // ﾀﾞｳﾝﾛｰﾄﾞ
    210                try {
    211                        // 2018/05/07 ADD
    212                        filePath = editPath(filePath);
    213
    214                        S3Object object = client.getObject(s3bucket, filePath);
    215
    216                        is = object.getObjectContent();
    217                } catch (Exception e) {
    218                        StringBuilder sbErrMsg = new StringBuilder();
    219                        sbErrMsg.append("ストレージからのファイルダウンロードに失敗しました。filePath:");
    220                        sbErrMsg.append(filePath);
    221                        sbErrMsg.append(" errInfo:");
    222                        sbErrMsg.append(e);
    223                        throw new HybsSystemException(sbErrMsg.toString());
    224                }
    225
    226                return is;
    227        }
    228
    229        /**
    230         * ｺﾋﾟｰ
    231         *
    232         * @og.rev 5.9.32.1 (2018/05/11)
    233         * @param oldFilePath   ｺﾋﾟｰ元ﾌｧｲﾙﾊﾟｽ
    234         * @param newFilePath   ｺﾋﾟｰ先ﾌｧｲﾙﾊﾟｽ
    235         * @param hsession              ｾｯｼｮﾝ
    236         */
    237        @Override
    238        public void copy(String oldFilePath, String newFilePath, HttpSession hsession) {
    239                try {
    240                        // 2018/05/07 ADD
    241                        oldFilePath = editPath(oldFilePath);
    242                        newFilePath = editPath(newFilePath);
    243
    244                        final CopyObjectRequest copyRequest = new CopyObjectRequest(s3bucket, oldFilePath, s3bucket, newFilePath);
    245                        client.copyObject(copyRequest);
    246                } catch (Exception e) {
    247                        StringBuilder sbErrMsg = new StringBuilder();
    248                        sbErrMsg.append("ストレージのファイルコピー処理に失敗しました。oldFilePath:");
    249                        sbErrMsg.append(oldFilePath);
    250                        sbErrMsg.append(" newFilePath:");
    251                        sbErrMsg.append(newFilePath);
    252                        sbErrMsg.append(" errInfo:");
    253                        sbErrMsg.append(e);
    254                        throw new HybsSystemException(sbErrMsg.toString());
    255                }
    256        }
    257
    258        /**
    259         * 削除
    260         *
    261         * @og.rev 5.9.32.1 (2018/05/11)
    262         * @param filePath      削除ﾌｧｲﾙのﾊﾟｽ
    263         * @param hsession      ｾｯｼｮﾝ
    264         */
    265        @Override
    266        public void delete(String filePath, HttpSession hsession) {
    267                // 削除
    268                try {
    269                        // 2018/05/07 ADD
    270                        filePath = editPath(filePath);
    271
    272                        final DeleteObjectRequest deleteRequest = new DeleteObjectRequest(s3bucket, filePath);
    273                        client.deleteObject(deleteRequest);
    274                        client.deleteObject(s3bucket, filePath);
    275                } catch (Exception e) {
    276                        StringBuilder sbErrMsg = new StringBuilder();
    277                        sbErrMsg.append("ストレージのファイル削除に失敗しました。filePath:");
    278                        sbErrMsg.append(filePath);
    279                        sbErrMsg.append(" errInfo:");
    280                        sbErrMsg.append(e);
    281                        throw new HybsSystemException(sbErrMsg.toString());
    282                }
    283        }
    284
    285        /**
    286         * ﾌｧｲﾙ名変更
    287         *
    288         * @param filePath              ﾌｧｲﾙﾊﾟｽ
    289         * @param oldFileName   変更前ﾌｧｲﾙ名
    290         * @param newFileName   変更後ﾌｧｲﾙ名
    291         * @param useBackup     変更後ﾌｧｲﾙ名が既に存在する場合のﾊﾞｯｸｱｯﾌﾟ作成ﾌﾗｸﾞ
    292         * @param hsession              ｾｯｼｮﾝ
    293         */
    294        public void rename(String filePath, String oldFileName, String newFileName, final boolean useBackup,
    295                        HttpSession hsession) {
    296                String newFilePath = filePath + newFileName;
    297                String oldFilePath = filePath + oldFileName;
    298
    299                // 変更先のファイルが存在した場合の処理
    300                if (exists(newFilePath, hsession)) {
    301                        // バックアップ作成する場合
    302                        if (useBackup) {
    303                                // バックアップファイル名は、元のファイル名(拡張子含む) ＋ "_" + 現在時刻のlong値 + "." +
    304                                // 元のファイルの拡張子
    305                                String bkupPath = filePath + "_backup/" + newFileName + "_" + System.currentTimeMillis()
    306//                                              + FileUtil.EXTENSION_SEPARATOR + FileUtil.getExtension(newFileName);
    307                                                + FileInfo.EXTENSION_SEPARATOR + FileInfo.getSUFIX(newFileName);
    308                                // バックアップフォルダに移動
    309                                copy(newFilePath, bkupPath, hsession);
    310                        }
    311                }
    312
    313                // コピー
    314                copy(oldFilePath, newFilePath, hsession);
    315                // 削除
    316                delete(oldFilePath, hsession);
    317        }
    318
    319        /**
    320         * ﾌｧｲﾙ存在ﾁｪｯｸ
    321         *
    322         * @og.rev 5.9.32.1 (2018/05/11)
    323         * @param filePath                      ﾌｧｲﾙﾊﾟｽ
    324         * @param hsession              ｾｯｼｮﾝ
    325         * @return                              true:存在 false:存在しない
    326         */
    327//      @Override
    328        public boolean exists(String filePath, HttpSession hsession) {
    329                boolean blnRtn = true;
    330                try {
    331                        // 2018/05/07 ADD
    332                        filePath = editPath(filePath);
    333
    334                        if (!client.doesObjectExist(s3bucket, filePath)) {
    335                                // ﾌｧｲﾙが取得できなかった場合は、falseを設定
    336                                blnRtn = false;
    337                        }
    338                } catch (Exception e) {
    339                        StringBuilder sbErrMsg = new StringBuilder();
    340                        sbErrMsg.append("ストレージのファイル取得に失敗しました。filePath:");
    341                        sbErrMsg.append(filePath);
    342                        sbErrMsg.append(" errInfo:");
    343                        sbErrMsg.append(e);
    344                        throw new HybsSystemException(sbErrMsg.toString());
    345                }
    346
    347                return blnRtn;
    348        }
    349
    350        /**
    351         * ﾌｧｲﾙ一覧取得
    352         *
    353         * @param startsWith    ﾊﾟｽの前方一致
    354         * @param hsession              ｾｯｼｮﾝ
    355         * @return                              ﾌｧｲﾙﾊﾟｽ一覧
    356         */
    357        @Override
    358        public String[] list(String startsWith, HttpSession hsession) {
    359                // 認証
    360                List<String> rtnList = new ArrayList<String>();
    361                try{
    362                        // 2018/05/07 ADD
    363                        startsWith = editPath(startsWith);
    364
    365                        ListObjectsV2Request request = new ListObjectsV2Request()
    366                                        .withBucketName(s3bucket)
    367                                        .withPrefix(startsWith);
    368                        ListObjectsV2Result list = client.listObjectsV2(request);
    369                        List<S3ObjectSummary> objects =  list.getObjectSummaries();
    370                        // 一覧の取得
    371                        for(S3ObjectSummary obj: objects){
    372                                // 名称を格納
    373                                rtnList.add(obj.getKey());
    374                        }
    375                } catch (Exception e){
    376                        StringBuilder sbErrMsg = new StringBuilder();
    377                        sbErrMsg.append("ファイル一覧の取得に失敗しました。startsWith:");
    378                        sbErrMsg.append(startsWith);
    379                        sbErrMsg.append(" errInfo:");
    380                        sbErrMsg.append("e");
    381                        throw new HybsSystemException(sbErrMsg.toString());
    382                }
    383                return rtnList.toArray(new String[rtnList.size()]);
    384        }
    385
    386        /**
    387         * ﾌｧｲﾙ情報取得
    388         *
    389         * @og.rev 5.9.32.1 (2018/05/11)
    390         * @param path                  ﾌｧｲﾙﾊﾟｽ
    391         * @param hsession              ｾｯｼｮﾝ
    392         * @return                              ﾌｧｲﾙ情報格納Map
    393         */
    394//      @Override
    395        public Map<String, String> getInfo(String path, HttpSession hsession) {
    396                Map<String, String> rtnMap = new HashMap<String,String>();
    397
    398                ObjectMetadata meta = null;
    399                try{
    400                        // 2018/05/07 ADD
    401                        path = editPath(path);
    402
    403                        // ﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄの取得
    404                        meta = client.getObjectMetadata(s3bucket, path);
    405                }catch(Exception e){
    406                        StringBuilder sbErrMsg = new StringBuilder();
    407                        sbErrMsg.append("ファイルの取得に失敗しました。path:");
    408                        sbErrMsg.append(path);
    409                        sbErrMsg.append(" errInfo:");
    410                        sbErrMsg.append(e);
    411                        throw new HybsSystemException(sbErrMsg.toString());
    412                }
    413                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
    414
    415                // ﾌｧｲﾙｻｲｽﾞ
    416                rtnMap.put(FILEINFO_SIZE, String.valueOf(meta.getContentLength()));
    417                // 最終更新時刻
    418                rtnMap.put(FILEINFO_LASTMODIFIED, sdf.format(meta.getLastModified()));
    419
    420                return rtnMap;
    421        }
    422}