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