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