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