001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.cloud; 017 018import java.io.ByteArrayInputStream; 019import java.io.File; 020import java.io.FileFilter; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.InputStream; 024import java.util.ArrayList; 025import java.util.List; 026 027import org.opengion.fukurou.model.FileOperation; 028import org.opengion.fukurou.system.Closer; // 8.0.0.0 (2021/09/30) util.Closer → system.Closer 029import org.opengion.fukurou.util.StringUtil; 030import org.opengion.hayabusa.common.HybsSystem; 031import org.opengion.hayabusa.common.HybsSystemException; 032 033import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // HybsSystem.BUFFER_MIDDLE は fukurou に移動 034import static org.opengion.fukurou.system.HybsConst.CR ; // 8.0.0.1 (2021/10/08) 035 036import com.amazonaws.auth.InstanceProfileCredentialsProvider; 037import com.amazonaws.services.s3.AmazonS3; 038import com.amazonaws.services.s3.AmazonS3ClientBuilder; 039import com.amazonaws.services.s3.model.AmazonS3Exception; 040import com.amazonaws.services.s3.model.ListObjectsV2Request; 041import com.amazonaws.services.s3.model.ListObjectsV2Result; 042import com.amazonaws.services.s3.model.ObjectListing; 043import com.amazonaws.services.s3.model.ObjectMetadata; 044import com.amazonaws.services.s3.model.PutObjectRequest; 045import com.amazonaws.services.s3.model.S3Object; 046import com.amazonaws.services.s3.model.S3ObjectSummary; 047 048/** 049 * FileOperation_AWSは、S3ストレージに対して、 050 * ファイル操作を行うクラスです。 051 * 052 * 認証は下記の2通りが可能です。→ 1) の方法のみサポートします。 053 * 1)実行サーバのEC2のインスタンスに、S3ストレージのアクセス許可を付与する 054 * <del> 2)システムリソースにアクセスキー・シークレットキー・エンドポイント・レギオンを登録する 055 * (CLOUD_STORAGE_S3_ACCESS_KEY、CLOUD_STORAGE_S3_SECRET_KEY、CLOUD_STORAGE_S3_SERVICE_END_POINT、CLOUD_STORAGE_S3_REGION) 056 * </del> 057 * 058 * 注意: 059 * バケット名は全ユーザで共有のため、自身のバケット名か、作成されていないバケット名を指定する必要があります。 060 * 061 * @og.rev 5.10.8.0 (2019/02/01) 新規作成 062 * 063 * @version 5 064 * @author oota 065 * @since JDK7.0 066 */ 067public class FileOperation_AWS extends CloudFileOperation { 068 private static final long serialVersionUID = 5108020190201L ; 069 070 private static final String PLUGIN = "AWS"; // 8.0.0.1 (2021/10/08) staticと大文字化 071 072 /** クラス変数 */ 073 private final AmazonS3 amazonS3; 074 075 /** 076 * コンストラクター 077 * 078 * 初期化処理です。 079 * AWSの認証処理を行います。 080 * 081 * @og.rev 8.0.0.1 (2021/10/08) CLOUD_STORAGE_S3… 関連の方法廃止 082 * 083 * @param bucket バケット 084 * @param inPath パス 085 */ 086 public FileOperation_AWS(final String bucket, final String inPath) { 087 super(StringUtil.nval(bucket, HybsSystem.sys("CLOUD_BUCKET")), inPath); 088 089 // IAMロールによる認証 090 amazonS3 = AmazonS3ClientBuilder.standard() 091 .withCredentials(new InstanceProfileCredentialsProvider(false)) 092 .build(); 093 094 try { 095 // S3に指定されたバケット(コンテナ)が存在しない場合は、作成する 096 if (!amazonS3.doesBucketExistV2(conBucket)) { // doesBucketExistV2最新JARだと出ている 097 amazonS3.createBucket(conBucket); 098 } 099 } catch (final AmazonS3Exception ase) { 100 final String errMsg = new StringBuilder(BUFFER_MIDDLE) 101 .append("IAMロールによる認証が失敗しました。").append( CR ) 102 .append( inPath ).toString(); 103 104 throw new HybsSystemException(errMsg,ase); 105 } 106 } 107 108 /** 109 * 書き込み処理(評価用) 110 * 111 * Fileを書き込みます。 112 * 113 * @og.rev 8.0.0.1 (2021/10/08) 新規追加 114 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 115 * 116 * @param inFile 書き込みFile 117 * @throws IOException ファイル関連エラー情報 118 */ 119 @Override 120 public void write(final File inFile) throws IOException { 121 try { 122 amazonS3.putObject(conBucket, conPath, inFile); 123// } catch (final Exception ex) { 124 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 125 final String errMsg = new StringBuilder(BUFFER_MIDDLE) 126 .append("AWSバケットに(File)書き込みが失敗しました。").append( CR ) 127 .append(conPath).toString(); 128 throw new IOException(errMsg,th); 129 } 130 } 131 132 /** 133 * 書き込み処理 134 * 135 * InputStreamのデータを書き込みます。 136 * 137 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 138 * 139 * @param is 書き込みデータのInputStream 140 * @throws IOException ファイル関連エラー情報 141 */ 142 @Override 143 public void write(final InputStream is) throws IOException { 144 ByteArrayInputStream bais = null; 145 try { 146 final ObjectMetadata om = new ObjectMetadata(); 147 148 final byte[] bytes = toByteArray(is); 149 om.setContentLength(bytes.length); 150 bais = new ByteArrayInputStream(bytes); 151 152 final PutObjectRequest request = new PutObjectRequest(conBucket, conPath, bais, om); 153 154 amazonS3.putObject(request); 155// } catch (final Exception ex) { 156 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 157 final String errMsg = new StringBuilder(BUFFER_MIDDLE) 158 .append("AWSバケットに(InputStream)書き込みが失敗しました。").append( CR ) 159 .append(conPath).toString(); 160 throw new IOException(errMsg,th); 161 } finally { 162 Closer.ioClose(bais); 163 } 164 } 165 166 /** 167 * 読み込み処理 168 * 169 * データを読み込み、InputStreamとして、返します。 170 * 171 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 172 * 173 * @return 読み込みデータのInputStream 174 * @throws FileNotFoundException ファイル非存在エラー情報 175 */ 176 @Override 177 public InputStream read() throws FileNotFoundException { 178 S3Object object = null; 179 180 try { 181 object = amazonS3.getObject(conBucket, conPath); 182// } catch (final Exception ex) { 183 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 184 final String errMsg = new StringBuilder(BUFFER_MIDDLE) 185 .append("AWSバケットから読み込みが失敗しました。").append( CR ) 186 .append(conPath).append( CR ) 187 .append( th.getMessage() ).toString(); 188 throw new FileNotFoundException(errMsg); // FileNotFoundException は、Throwable 引数を持つコンストラクタはなない。 189 } 190 return object.getObjectContent(); // com.amazonaws.services.s3.model.S3ObjectInputStream 191 } 192 193 /** 194 * 削除処理 195 * 196 * ファイルを削除します。 197 * 198 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 199 * 200 * @return 成否フラグ 201 */ 202 @Override 203 public boolean delete() { 204 boolean flgRtn = false; 205 206 try { 207 if (isFile()) { 208 // ファイル削除 209 amazonS3.deleteObject(conBucket, conPath); 210 } else if (isDirectory()) { 211 // ディレクトリ削除 212 // 一括削除のapiが無いので、繰り返しで削除を行う 213 final ObjectListing objectList = amazonS3.listObjects(conBucket, conPath); 214 final List<S3ObjectSummary> list = objectList.getObjectSummaries(); 215 for (final S3ObjectSummary obj : list) { 216 amazonS3.deleteObject(conBucket, obj.getKey()); 217 } 218 } 219 flgRtn = true; 220// } catch (final Exception ex) { 221 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 222 // エラーはスルーして、falseを返す 223 System.out.println( th.getMessage() ); 224 } 225 226 return flgRtn; 227 } 228 229 /** 230 * コピー処理 231 * 232 * ファイルを指定先に、コピーします。 233 * 234 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 235 * 236 * @param afPath コピー先 237 * @return 成否フラグ 238 */ 239 @Override 240 public boolean copy(final String afPath) { 241 boolean flgRtn = false; 242 243 try { 244 amazonS3.copyObject(conBucket, conPath, conBucket, afPath); 245 flgRtn = true; 246// } catch (final Exception ex) { 247 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 248 // エラーはスルーして、falseを返す 249 System.out.println( th.getMessage() ); 250 } 251 252 return flgRtn; 253 } 254 255 /** 256 * ファイルサイズ取得 257 * 258 * ファイルサイズを返します。 259 * 260 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 261 * 262 * @return ファイルサイズ 263 */ 264 @Override 265 public long length() { 266 long rtn = 0; 267 268 try { 269 final ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath); 270 rtn = meta.getContentLength(); 271// } catch (final Exception ex) { 272 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 273 // エラーはスルーして、0を返す。 274 System.out.println( th.getMessage() ); 275 } 276 return rtn; 277 } 278 279 /** 280 * 最終更新時刻取得 281 * 282 * 最終更新時刻を取得します。 283 * 284 * @og.rev 8.0.2.0 (2021/11/30) catch Exception → Throwable に変更。 285 * 286 * @return 最終更新時刻 287 */ 288 @Override 289 public long lastModified() { 290 long rtn = 0; 291 292 try { 293 final ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath); 294 rtn = meta.getLastModified().getTime(); 295// } catch (final Exception ex) { 296 } catch( final Throwable th ) { // 8.0.2.0 (2021/11/30) 297 // エラーはスルーして、0を返す 298 System.out.println( th.getMessage() ); 299 } 300 return rtn; 301 } 302 303 /** 304 * ファイル判定 305 * 306 * ファイルの場合は、trueを返します。 307 * 308 * @return ファイル判定フラグ 309 */ 310 @Override 311 public boolean isFile() { 312 boolean rtn = false; 313 314 if(!isDirectory()) { 315 rtn = amazonS3.doesObjectExist(conBucket, conPath); 316 } 317 318 return rtn; 319 } 320 321 /** 322 * ディレクトリ判定 323 * 324 * ディレクトリの場合は、trueを返します。 325 * 326 * @return ディレクトリ判定フラグ 327 */ 328 @Override 329 public boolean isDirectory() { 330 if (StringUtil.isEmpty(conPath)) { // 8.0.1.0 (2021/10/29) org.apache.commons.lang3.StringUtils 置換 331 return true; 332 } 333 334 // S3にはディレクトリの概念はないので、「/」で続くデータが存在するかで、判定 335 final ObjectListing objectList = amazonS3.listObjects(conBucket, setDirTail(conPath)); 336 final List<S3ObjectSummary> list = objectList.getObjectSummaries(); 337 338 return list.size() != 0 ; 339 } 340 341 /** 342 * ファイル一覧取得 343 * 344 * パスのファイルとディレクトリ一覧を取得します。 345 * 346 * @og.rev 8.0.2.0 (2021/11/30) fukurou.util.rTrim(String,char) 使用 347 * 348 * @param filter フィルタ情報 349 * @return ファイルとティレクトリ一覧 350 */ 351 @Override 352 public File[] listFiles(final FileFilter filter) { 353 if (!exists()) { 354 return new FileOperationInfo[0]; 355 } 356 357 String search = conPath; 358 if (isDirectory()) { 359 search = setDirTail(conPath); 360 } 361 362 final List<File> rtnList = new ArrayList<File>(); 363 364 // 検索処理 365 final ListObjectsV2Request request = new ListObjectsV2Request() 366 .withBucketName(conBucket) 367 .withPrefix(search) 368 .withDelimiter("/"); 369 final ListObjectsV2Result list = amazonS3.listObjectsV2(request); 370 final List<S3ObjectSummary> objects = list.getObjectSummaries(); 371 372 // ファイル情報の取得 373 for (final S3ObjectSummary obj : objects) { 374 final String key = obj.getKey(); 375 376 final FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key); 377 file.setLastModifiedValue(obj.getLastModified().getTime()); 378 file.setFile(true); 379 file.setSize(obj.getSize()); 380 rtnList.add(file); 381 } 382 383 // ディレクトリ情報の取得 384 final List<String> folders = list.getCommonPrefixes(); 385 for (final String str : folders) { 386// final String key = rTrim(str, '/'); 387 final String key = StringUtil.rTrim(str, '/'); // 8.0.2.0 (2021/11/30) 388 389 final FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key); 390 file.setDirectory(true); 391 rtnList.add(file); 392 } 393 394 // フィルタ処理 395 return filter(rtnList, filter); 396 } 397 398 /** 399 * 親ディレクトリ情報の取得 400 * 401 * 親のディレクトリを返します。 402 * 403 * @return 親のディレクトリ情報 404 */ 405 @Override 406 public FileOperation getParentFile() { 407 return new FileOperation_AWS(conBucket, this.getParent()); 408 } 409}