001package org.opengion.plugin.cloud;
002
003import java.io.ByteArrayInputStream;
004import java.io.FileNotFoundException;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.ArrayList;
008import java.util.List;
009
010import org.apache.commons.lang3.StringUtils;
011import org.opengion.fukurou.model.AbstractFileOperation;
012import org.opengion.fukurou.model.FileOperationFileFilter;
013import org.opengion.fukurou.model.FileOperationInfo;
014import org.opengion.fukurou.model.FileOperation;
015import org.opengion.fukurou.util.Closer;
016import org.opengion.fukurou.util.StringUtil;
017import org.opengion.hayabusa.common.HybsSystem;
018import org.opengion.hayabusa.common.HybsSystemException;
019
020import com.ibm.cloud.objectstorage.SDKGlobalConfiguration;
021import com.ibm.cloud.objectstorage.auth.AWSCredentials;
022import com.ibm.cloud.objectstorage.auth.AWSStaticCredentialsProvider;
023import com.ibm.cloud.objectstorage.client.builder.AwsClientBuilder.EndpointConfiguration;
024import com.ibm.cloud.objectstorage.oauth.BasicIBMOAuthCredentials;
025import com.ibm.cloud.objectstorage.services.s3.AmazonS3;
026import com.ibm.cloud.objectstorage.services.s3.AmazonS3ClientBuilder;
027import com.ibm.cloud.objectstorage.services.s3.model.AmazonS3Exception;
028import com.ibm.cloud.objectstorage.services.s3.model.ListObjectsV2Request;
029import com.ibm.cloud.objectstorage.services.s3.model.ListObjectsV2Result;
030import com.ibm.cloud.objectstorage.services.s3.model.ObjectListing;
031import com.ibm.cloud.objectstorage.services.s3.model.ObjectMetadata;
032import com.ibm.cloud.objectstorage.services.s3.model.PutObjectRequest;
033import com.ibm.cloud.objectstorage.services.s3.model.S3Object;
034import com.ibm.cloud.objectstorage.services.s3.model.S3ObjectSummary;
035
036/**
037 * FileOperation_IBM.javaは、Bluemixのストレージ(S3と互換性があります)の、
038 * ファイル操作を行うクラスです。
039 * 
040 * IBMCloud(Bluemix)のダッシュボードから、下記の値を取得して、
041 * システムリソースに登録を行う必要があります。
042 * 
043 * CLOUD_STORAGE_S3_APIKEY、CLOUD_STORAGE_S3_SERVICEINSTANCEID、CLOUD_STORAGE_S3_SERVICE_END_POINT、CLOUD_STORAGE_S3_REGION
044 * 
045 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
046 *
047 * @version 5
048 * @author  oota
049 * @since   JDK7.0
050 *
051 */
052public class FileOperation_IBM extends AbstractFileOperation {
053        /** クラス変数 */
054        private final AmazonS3 amazonS3;
055        private final String conBuket;
056        private static final String COS_AUTH_ENDPOINT = "https://iam.ng.bluemix.net/oidc/token";
057
058        /**
059         * コンストラクター
060         * @param buket バケット
061         * @param inPath パス
062         */
063        public FileOperation_IBM(String buket, String inPath) {
064                super(StringUtil.nval( buket, HybsSystem.sys("CLOUD_BUCKET") ),inPath);
065                conBuket = buket;
066
067                // 公式のマニュアルにより、下記のENDPOINTを設定
068                SDKGlobalConfiguration.IAM_ENDPOINT = COS_AUTH_ENDPOINT;
069
070                final String apiKey = HybsSystem.sys("CLOUD_STORAGE_S3_APIKEY");
071                final String serviceInstanceId = HybsSystem.sys("CLOUD_STORAGE_S3_SERVICEINSTANCEID");
072                AWSCredentials credentials = new BasicIBMOAuthCredentials(apiKey, serviceInstanceId);
073
074                final String serviceEndpoint = HybsSystem.sys("CLOUD_STORAGE_S3_SERVICE_END_POINT");
075                final String signingRegion = HybsSystem.sys("CLOUD_STORAGE_S3_REGION");
076                EndpointConfiguration endpointConfiguration = new EndpointConfiguration(serviceEndpoint, signingRegion);
077
078                amazonS3 = AmazonS3ClientBuilder.standard()
079                                .withCredentials(new AWSStaticCredentialsProvider(credentials))
080                                .withPathStyleAccessEnabled(true)
081                                .withEndpointConfiguration(endpointConfiguration)
082                                .build();
083
084                try {
085                        if (!amazonS3.doesBucketExist(bucket)) {
086                                amazonS3.createBucket(bucket);
087                        }
088                } catch (AmazonS3Exception ase) {
089                        StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
090                        errMsg.append("アクセスキーによる認証が失敗しました。");
091                        errMsg.append(" CLOUD_STORAGE_S3_APIKEY:").append(apiKey);
092                        errMsg.append(" CLOUD_STORAGE_S3_SERVICEINSTANCEID:").append(serviceInstanceId);
093                        errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT:").append(serviceEndpoint);
094                        errMsg.append(" CLOUD_STORAGE_S3_REGION:").append(signingRegion);
095                        errMsg.append(" システムエラー情報:").append(ase.getMessage());
096                        throw new HybsSystemException(errMsg.toString());
097                }
098        }
099
100        /**
101         * InputStreamのデータを書き込みます。
102         * 
103         * @param is 書き込みデータのInputStream
104         * @throws IOException
105         */
106        @Override
107        public void write(InputStream is) throws IOException {
108                ByteArrayInputStream bais = null;
109                try {
110                        ObjectMetadata om = new ObjectMetadata();
111
112                        byte[] bytes = toByteArray(is);
113                        om.setContentLength(bytes.length);
114                        bais = new ByteArrayInputStream(bytes);
115
116                        PutObjectRequest request = new PutObjectRequest(bucket, path, bais, om);
117
118                        amazonS3.putObject(request);
119                } catch (Exception e) {
120                        StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
121                        errMsg.append("BLUEMIXバケットに書き込みが失敗しました。path:").append(path);
122                        errMsg.append(" システムエラー情報:").append(e.getMessage());
123                        throw new IOException(errMsg.toString());
124                } finally {
125                        Closer.ioClose(bais);
126                }
127        }
128
129        /**
130         * データを読み込み、InputStreamとして、返します。
131         * 
132         * @return 読み込みデータのInputStream
133         * @throws FileNotFoundException
134         */
135        @Override
136        public InputStream read() throws FileNotFoundException {
137                S3Object object = null;
138
139                try {
140                        object = amazonS3.getObject(bucket, path);
141                } catch (Exception e) {
142                        StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
143                        errMsg.append("BLUEMIXバケットから読み込みが失敗しました。path:").append(path);
144                        errMsg.append(" システムエラー情報:").append(e.getMessage());
145                        throw new FileNotFoundException(errMsg.toString());
146                }
147                return object.getObjectContent();
148        }
149
150        /**
151         * ファイルを削除します。
152         * 
153         * @return 成否フラグ
154         */
155        @Override
156        public boolean delete() {
157                boolean flgRtn = false;
158
159                try {
160                        if (isFile()) {
161                                // ファイル削除
162                                amazonS3.deleteObject(bucket, path);
163                        } else if (isDirectory()) {
164                                // ディレクトリ削除
165                                // 一括削除のapiが無いので、繰り返しで削除を行う
166                                ObjectListing objectList = amazonS3.listObjects(bucket, path);
167                                List<S3ObjectSummary> list = objectList.getObjectSummaries();
168                                for (S3ObjectSummary obj : list) {
169                                        amazonS3.deleteObject(bucket, obj.getKey());
170                                }
171
172                        }
173                        flgRtn = true;
174                } catch (Exception e) {
175                        // エラーはスルーして、falseを返す
176                }
177
178                return flgRtn;
179        }
180
181        /**
182         * ファイルを指定先に、コピーします。
183         * 
184         * @param afPath コピー先
185         * @return 成否フラグ
186         */
187        @Override
188        public boolean copy(String afPath) {
189                boolean flgRtn = false;
190
191                try {
192                        amazonS3.copyObject(bucket, path, bucket, afPath);
193                        flgRtn = true;
194                } catch (Exception e) {
195                        // エラーはスルーして、falseを返す
196                }
197
198                return flgRtn;
199        }
200
201        /**
202         * ファイルサイズを返します
203         * 
204         * @return ファイルサイズ
205         */
206        @Override
207        public long length() {
208                long rtn = 0;
209
210                try {
211                        ObjectMetadata meta = amazonS3.getObjectMetadata(bucket, path);
212                        rtn = meta.getContentLength();
213                } catch (Exception e) {
214                        // エラーはスルーして、0を返す。
215                }
216                return rtn;
217        }
218
219        /**
220         * 最終更新時刻を取得します。
221         * 
222         * @return 最終更新時刻
223         */
224        @Override
225        public long lastModified() {
226                long rtn = 0;
227
228                try {
229                        ObjectMetadata meta = amazonS3.getObjectMetadata(bucket, path);
230                        rtn = meta.getLastModified().getTime();
231                } catch (Exception e) {
232                        // エラーはスルーして、0を返す
233                }
234                return rtn;
235        }
236
237        /**
238         * ファイルの場合は、trueを返します。
239         * 
240         * @return ファイルフラグ
241         */
242        @Override
243        public boolean isFile() {
244                return amazonS3.doesObjectExist(bucket, path);
245        }
246
247        /**
248         * ディレクトリの場合は、trueを返します。
249         * 
250         * @return ディレクトリフラグ
251         */
252        @Override
253        public boolean isDirectory() {
254                boolean flgRtn = false;
255
256                if (StringUtils.isEmpty(path)) {
257                        return true;
258                }
259
260                // S3にはディレクトリの概念はないので、「/」で続くデータが存在するかで、判定
261                ObjectListing objectList = amazonS3.listObjects(bucket, setDirTail(path));
262                List<S3ObjectSummary> list = objectList.getObjectSummaries();
263                flgRtn = list.size() == 0 ? false : true;
264
265                return flgRtn;
266        }
267
268        /**
269         * パスのファイルとディレクトリ一覧を取得します。
270         * 
271         * @return ファイルとティレクトリ一覧
272         */
273        @Override
274        public FileOperation[] listFiles(FileOperationFileFilter filter) {
275                if (!exists()) {
276                        return new FileOperationInfo[0];
277                }
278
279                String search = path;
280                if (isDirectory()) {
281                        search = setDirTail(path);
282                }
283
284                List<FileOperationInfo> rtnList = new ArrayList<FileOperationInfo>();
285
286                // 検索処理
287                ListObjectsV2Request request = new ListObjectsV2Request()
288                                .withBucketName(bucket)
289                                .withPrefix(search)
290                                .withDelimiter("/");
291                ListObjectsV2Result list = amazonS3.listObjectsV2(request);
292                List<S3ObjectSummary> objects = list.getObjectSummaries();
293
294                // ファイル情報の取得
295                for (S3ObjectSummary obj : objects) {
296                        String key = obj.getKey();
297
298                        FileOperationInfo file = new FileOperationInfo();
299                        file.setPath(key);
300                        file.setName(drawName(key));
301                        file.setParent(drawParent(key));
302                        file.setLastModified(obj.getLastModified().getTime());
303                        file.setFile(true);
304                        file.setSize(obj.getSize());
305                        rtnList.add(file);
306                }
307
308                // サブディレクトリ情報の取得
309                List<String> folders = list.getCommonPrefixes();
310                for (String str : folders) {
311                        String key = rTrim(str, '/');
312
313                        FileOperationInfo file = new FileOperationInfo();
314                        file.setPath(key);
315                        file.setName(drawName(key));
316                        file.setParent(drawParent(key));
317                        file.setDirectory(true);
318                        rtnList.add(file);
319                }
320
321                // フィルタ処理
322                FileOperation[] filterList = filter(rtnList, filter);
323
324                return filterList;
325        }
326
327        /**
328         * 親のディレクトリを返します。
329         * 
330         * @return 親のディレクトリ
331         */
332        @Override
333        public FileOperation getParentFile() {
334                return new FileOperation_IBM(conBuket,getParent());
335        }
336
337        /** Bluemixの場合はローカル環境でのテストは出来ないようです。(proxyを設定しても接続できません) */
338}