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}