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
052 * @author  oota
053 * @sinse   JDK7.0
054 */
055public class FileOperation_AWS extends CloudFileOperation {
056        /** クラス変数 */
057        private final AmazonS3 amazonS3;
058        private final String PLUGIN = "aws";
059
060        /**
061         * コンストラクター
062         * 
063         * 初期化処理です。
064         * AWSの認証処理を行います。
065         * 
066         * @param bucket バケット
067         * @param inPath パス
068         */
069        public FileOperation_AWS(String bucket, String inPath) {
070                super(StringUtil.nval(bucket, HybsSystem.sys("CLOUD_BUCKET")), inPath);
071                setPlugin(PLUGIN);
072
073                // アクセスキー
074                final String s3AccessKey = HybsSystem.sys("CLOUD_STORAGE_S3_ACCESS_KEY");
075                String s3SecretKey = "";
076                String s3ServiceEndPoint = "";
077                String s3Region = "";
078
079                // S3アクセスクライアントの生成
080                if (StringUtils.isEmpty(s3AccessKey)) {
081                        // IAMロールによる認証
082                        amazonS3 = AmazonS3ClientBuilder.standard()
083                                        .withCredentials(new InstanceProfileCredentialsProvider(false))
084                                        .build();
085                } else {
086                        // リソースのアクセスキーによる認証
087                        // シークレットキー
088                        s3SecretKey = HybsSystem.sys("CLOUD_STORAGE_S3_SECRET_KEY");
089                        // エンドポイント
090                        s3ServiceEndPoint = HybsSystem.sys("CLOUD_STORAGE_S3_SERVICE_END_POINT");
091                        // レギオン
092                        s3Region = HybsSystem.sys("CLOUD_STORAGE_S3_REGION");
093
094                        // AWSの認証情報
095                        AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
096
097                        // エンドポイント設定
098                        EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint, s3Region);
099                        amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials))
100                                        .withEndpointConfiguration(endpointConfiguration)
101                                        .build();
102                }
103
104                try {
105                        // S3に指定されたバケット(コンテナ)が存在しない場合は、作成する
106                        if (!amazonS3.doesBucketExist(conBucket)) { // doesBucketExistV2最新JARだと出ている
107                                amazonS3.createBucket(conBucket);
108                        }
109                } catch (AmazonS3Exception ase) {
110                        StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
111                        if (StringUtils.isEmpty(s3AccessKey)) {
112                                errMsg.append("IAMロールによる認証が失敗しました。");
113
114                        } else {
115                                errMsg.append("アクセスキーによる認証が失敗しました。");
116                                errMsg.append(" CLOUD_STORAGE_S3_ACCESS_KEY:").append(s3AccessKey);
117                                errMsg.append(" CLOUD_STORAGE_S3_SECRET_KEY:非表示");
118                                errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT:").append(s3ServiceEndPoint);
119                                errMsg.append(" CLOUD_STORAGE_S3_REGION:").append(s3Region);
120                        }
121                        errMsg.append(" システムエラー情報:").append(ase.getMessage());
122                        throw new HybsSystemException(errMsg.toString());
123                }
124        }
125
126        /**
127         * 書き込み処理
128         * 
129         * InputStreamのデータを書き込みます。
130         * 
131         * @param is 書き込みデータのInputStream
132         * @throws IOException ファイル関連エラー情報
133         */
134        @Override
135        public void write(InputStream is) throws IOException {
136                ByteArrayInputStream bais = null;
137                try {
138                        ObjectMetadata om = new ObjectMetadata();
139
140                        byte[] bytes = toByteArray(is);
141                        om.setContentLength(bytes.length);
142                        bais = new ByteArrayInputStream(bytes);
143
144                        PutObjectRequest request = new PutObjectRequest(conBucket, conPath, bais, om);
145
146                        amazonS3.putObject(request);
147                } catch (Exception e) {
148                        StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
149                        errMsg.append("AWSバケットに書き込みが失敗しました。path:").append(conPath);
150                        errMsg.append(" システムエラー情報:").append(e.getMessage());
151                        throw new IOException(errMsg.toString());
152                } finally {
153                        Closer.ioClose(bais);
154                }
155        }
156
157        /**
158         * 読み込み処理
159         * 
160         * データを読み込み、InputStreamとして、返します。
161         * 
162         * @return 読み込みデータのInputStream
163         * @throws FileNotFoundException ファイル非存在エラー情報
164         */
165        @Override
166        public InputStream read() throws FileNotFoundException {
167                S3Object object = null;
168
169                try {
170                        object = amazonS3.getObject(conBucket, conPath);
171                } catch (Exception e) {
172                        StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
173                        errMsg.append("AWSバケットから読み込みが失敗しました。path:").append(conPath);
174                        errMsg.append(" システムエラー情報:").append(e.getMessage());
175                        throw new FileNotFoundException(errMsg.toString());
176                }
177                return object.getObjectContent();
178        }
179
180        /**
181         * 削除処理
182         * 
183         * ファイルを削除します。
184         * 
185         * @return 成否フラグ
186         */
187        @Override
188        public boolean delete() {
189                boolean flgRtn = false;
190
191                try {
192                        if (isFile()) {
193                                // ファイル削除
194                                amazonS3.deleteObject(conBucket, conPath);
195                        } else if (isDirectory()) {
196                                // ディレクトリ削除
197                                // 一括削除のapiが無いので、繰り返しで削除を行う
198                                ObjectListing objectList = amazonS3.listObjects(conBucket, conPath);
199                                List<S3ObjectSummary> list = objectList.getObjectSummaries();
200                                for (S3ObjectSummary obj : list) {
201                                        amazonS3.deleteObject(conBucket, obj.getKey());
202                                }
203
204                        }
205                        flgRtn = true;
206                } catch (Exception e) {
207                        // エラーはスルーして、falseを返す
208                }
209
210                return flgRtn;
211        }
212
213        /**
214         * コピー処理
215         * 
216         * ファイルを指定先に、コピーします。
217         * 
218         * @param afPath コピー先
219         * @return 成否フラグ
220         */
221        @Override
222        public boolean copy(String afPath) {
223                boolean flgRtn = false;
224
225                try {
226                        amazonS3.copyObject(conBucket, conPath, conBucket, afPath);
227                        flgRtn = true;
228                } catch (Exception e) {
229                        // エラーはスルーして、falseを返す
230                }
231
232                return flgRtn;
233        }
234
235        /**
236         * ファイルサイズ取得
237         * 
238         * ファイルサイズを返します。
239         * 
240         * @return ファイルサイズ
241         */
242        @Override
243        public long length() {
244                long rtn = 0;
245
246                try {
247                        ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath);
248                        rtn = meta.getContentLength();
249                } catch (Exception e) {
250                        // エラーはスルーして、0を返す。
251                }
252                return rtn;
253        }
254
255        /**
256         * 最終更新時刻取得
257         * 
258         * 最終更新時刻を取得します。
259         * 
260         * @return 最終更新時刻
261         */
262        @Override
263        public long lastModified() {
264                long rtn = 0;
265
266                try {
267                        ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath);
268                        rtn = meta.getLastModified().getTime();
269                } catch (Exception e) {
270                        // エラーはスルーして、0を返す
271                }
272                return rtn;
273        }
274
275        /**
276         * ファイル判定
277         * 
278         * ファイルの場合は、trueを返します。
279         * 
280         * @return ファイル判定フラグ
281         */
282        @Override
283        public boolean isFile() {
284                boolean rtn = false;
285                
286                if(!isDirectory()) {
287                        rtn = amazonS3.doesObjectExist(conBucket, conPath); 
288                }
289                
290                return rtn;
291        }
292
293        /**
294         * ディレクトリ判定
295         * 
296         * ディレクトリの場合は、trueを返します。
297         * 
298         * @return ディレクトリ判定フラグ
299         */
300        @Override
301        public boolean isDirectory() {
302                boolean flgRtn = false;
303
304                if (StringUtils.isEmpty(conPath)) {
305                        return true;
306                }
307
308                // S3にはディレクトリの概念はないので、「/」で続くデータが存在するかで、判定
309                ObjectListing objectList = amazonS3.listObjects(conBucket, setDirTail(conPath));
310                List<S3ObjectSummary> list = objectList.getObjectSummaries();
311                flgRtn = list.size() == 0 ? false : true;
312
313                return flgRtn;
314        }
315
316        /**
317         * ファイル一覧取得
318         * 
319         * パスのファイルとディレクトリ一覧を取得します。
320         * 
321         * @param filter フィルタ情報
322         * @return ファイルとティレクトリ一覧
323         */
324        @Override
325        public File[] listFiles(FileFilter filter) {
326                if (!exists()) {
327                        return new FileOperationInfo[0];
328                }
329
330                String search = conPath;
331                if (isDirectory()) {
332                        search = setDirTail(conPath);
333                }
334
335                List<File> rtnList = new ArrayList<File>();
336
337                // 検索処理
338                ListObjectsV2Request request = new ListObjectsV2Request()
339                                .withBucketName(conBucket)
340                                .withPrefix(search)
341                                .withDelimiter("/");
342                ListObjectsV2Result list = amazonS3.listObjectsV2(request);
343                List<S3ObjectSummary> objects = list.getObjectSummaries();
344
345                // ファイル情報の取得
346                for (S3ObjectSummary obj : objects) {
347                        String key = obj.getKey();
348
349                        FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key);
350                        file.setLastModifiedValue(obj.getLastModified().getTime());
351                        file.setFile(true);
352                        file.setSize(obj.getSize());
353                        rtnList.add(file);
354                }
355
356                // ディレクトリ情報の取得
357                List<String> folders = list.getCommonPrefixes();
358                for (String str : folders) {
359                        String key = rTrim(str, '/');
360
361                        FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key);
362                        file.setDirectory(true);
363                        rtnList.add(file);
364                }
365
366                // フィルタ処理
367                File[] filterList = filter(rtnList, filter);
368
369                return filterList;
370        }
371
372        /**
373         * 親ディレクトリ情報の取得
374         * 
375         * 親のディレクトリを返します。
376         * 
377         * @return 親のディレクトリ情報
378         */
379        @Override
380        public FileOperation getParentFile() {
381                return new FileOperation_AWS(conBucket, this.getParent());
382        }
383
384        /** 以下はローカル環境でのテスト用メソッドです。 */
385//                              /**
386//                               * ローカルでのテスト用コンストラクタ
387//                               * 
388//                               * 要:super.bucketの値は固定値に変更してください。
389//                               * 
390//                               * @param inPath
391//                               */
392//                              public FileOperation_AWS(String bucket, String inPath, boolean test) {
393//                                      // super( StringUtil.nval( bucket, HybsSystem.sys("CLOUD_BUCKET") ), inPath);
394//                                      super( StringUtil.nval( bucket, "opengiontestbucket" ), inPath);
395//                      
396//                                      // aws接続情報
397//                                       String s3AccessKey = "[key]";
398//                                       String s3SecretKey = "[secret]";
399//                                      String s3ServiceEndPoint = "s3-ap-northeast-1.amazonaws.com";
400//                                      String s3Region = "ap-northeast-1";
401//                                      
402//                                      // proxy環境での設定
403//                                      ClientConfiguration conf = new ClientConfiguration();
404//                                      conf.setProtocol(Protocol.HTTPS);
405//                                      conf.setProxyHost("mtc-px14");
406//                                      conf.setProxyPort(8081);
407//                      
408//                                      // AWSの認証情報
409//                                      AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
410//                      
411//                                      // エンドポイント設定
412//                                      EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint, s3Region);
413//                                      amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials))
414//                                                      .withEndpointConfiguration(endpointConfiguration)
415//                                                      .withClientConfiguration(conf) // テスト用にproxy設定
416//                                                      .build();
417//                      
418//                                      try {
419//                                              // S3に指定されたバケット(コンテナ)が存在しない場合は、作成する
420//                                              if (!amazonS3.doesBucketExistV2(conBucket)) {
421//                                                      amazonS3.createBucket(conBucket);
422//                                              }
423//                                      } catch (AmazonS3Exception ase) {
424//                                              StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
425//                                              if (StringUtils.isEmpty(s3AccessKey)) {
426//                                                      errMsg.append("IAMロールによる認証か、バケットの作成が失敗しました。");
427//                                                      
428//                                              } else {
429//                                                      errMsg.append("アクセスキーによる認証かバケットの作成が失敗しました。");
430//                                                      errMsg.append(" CLOUD_STORAGE_S3_ACCESS_KEY:").append(s3AccessKey);
431//                                                      errMsg.append(" CLOUD_STORAGE_S3_SECRET_KEY:非表示");
432//                                                      errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT:").append(s3ServiceEndPoint);
433//                                                      errMsg.append(" CLOUD_STORAGE_S3_REGION:").append(s3Region);
434//                                              }
435//                                              errMsg.append(" バケット名:").append(conBucket);
436//                                              errMsg.append(" システムエラー情報:").append(ase.getMessage());
437//                                              throw new RuntimeException(errMsg.toString());
438//                                      }
439//                              }
440//                      
441//                              /** テスト用メソッド */
442//                              public static void main(String[] args) {
443////                                    writeTest();
444////                                    listTest();
445////                                    methodTest();
446//                                      copyTest();
447//                              }
448//                              
449//                              public static void copyTest() {
450//                                      File[] list = new File[] {new File("LIST01.xls"), new File("LIST02.ods")};
451//                                      
452////                                    System.out.println(file.exists());
453//                                      
454//                                      for(File file: list) {
455//                                              FileOperation file2 = new FileOperation_AWS("ootalocaltest", file.getName(), true);
456//                                              FileUtil.copy(file,  file2);
457//                                      }
458//                              }
459//                              
460//                              public static void writeTest() {
461//                                      FileOperation file = new FileOperation_AWS("otlocaltest01", "sample/test.txt", true);
462//                                      try(InputStream is = new ByteArrayInputStream("sample".getBytes())){
463//                                              file.write(is);
464//                                      }catch(Exception e) {
465//                                              System.out.println(e.getMessage());
466//                                      }
467//                                      
468////                                    File file 
469//                              }
470//                              
471//                              public static void listTest() {
472//                                      File file = new FileOperation_AWS("otlocaltest01", "sample", true);
473//                                      
474//                                      // フィルタ設定
475//                                      HybsFileFilter filter = new HybsFileFilter();
476//                                      filter.startsWith("te");
477//                                      
478//                                      File[] list = file.listFiles(filter);
479//                                      
480//                                      System.out.println(list.length);
481//                                      for(File f: list) {
482//                                              FileOperation fo = (FileOperation)f;
483//                                              
484//                                              try(InputStream is = fo.read()){
485//                                                      Files.copy(is, Paths.get("out.txt"), StandardCopyOption.REPLACE_EXISTING);
486//                                              }catch(IOException ioe) {
487//                                                      System.out.println(ioe);
488//                                              }
489//                                      }
490//                              }
491//                              
492//                              public static void methodTest() {
493//                                      FileOperation file = new FileOperation_AWS("otlocaltest01", "", true);
494//                                      boolean rtn = false;
495////                                    rtn = file.isDirectory();
496//                                      rtn = file.isFile();
497//                                      
498//                                      System.out.println(rtn);
499//                              }
500}