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 * 認証は下記の2通りが可能です。
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}