001package org.opengion.plugin.cloud;
002
003        // import java.io.IOException;
004        // import java.text.SimpleDateFormat;
005        // import java.util.Iterator;
006import java.io.InputStream;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.List;
010import java.util.Map;
011
012import javax.servlet.http.HttpSession;
013
014import org.opengion.hayabusa.io.StorageAPI;
015        // import org.opengion.fukurou.system.Closer;
016        // import org.opengion.fukurou.util.FileUtil;
017        // import org.opengion.hayabusa.common.HybsSystemException;
018        // 5.9.25.0 (2017/10/06) とりあえず、拡張jar は入れていません。
019        // import org.openstack4j.api.OSClient.OSClientV3;
020        // import org.openstack4j.api.storage.ObjectStorageService;
021        // import org.openstack4j.model.common.DLPayload;
022        // import org.openstack4j.model.common.Identifier;
023        // import org.openstack4j.model.common.Payload;
024        // import org.openstack4j.model.storage.object.SwiftObject;
025        // import org.openstack4j.model.storage.object.options.ObjectListOptions;
026        // import org.openstack4j.model.storage.object.options.ObjectLocation;
027        // import org.openstack4j.openstack.OSFactory;
028        // import org.openstack4j.openstack.internal.OSClientSession;
029
030        // import com.fasterxml.jackson.databind.JsonNode;
031        // import com.fasterxml.jackson.databind.ObjectMapper;
032
033/**
034 * bluemix用のクラウドストレージ操作実装
035 * 
036 * bluemix上での利用を想定しているため、ユーザ情報は環境変数VCAP_SERVICESから取得可能という前提です。
037 * この環境変数はbluemix上でオブジェクトストレージを接続設定する事で自動設定されます。
038 * 
039 * このクラスのコンパイルには
040 * openstack4j-core及び openstack4j-okhttpが必要です。
041 * 実行にはそれ以外に以下のモジュールが必要です。(バージョンは作成時のもの)
042 * btf-1.2.jar ,guava-20.0.jar, jackson-coreutils-1.6.jar, jackson-dataformat-yaml-2.8.8.jar, json-patch-1.9.jar, jsr305-2.0.0.jar
043 *      ,msg-simple-1.1.jar, okhttp-3.2.0.jar, okio-1.6.0.jar, slf4j-api-1.7.21.jar, slf4j-simple-1.7.21.jar, snakeyaml-1.15.jar
044 * 
045 *
046 * @og.group クラウド
047 * @og.rev 5.9.25.0 (2017/10/06) 新規作成
048 *
049 * @version 5.0
050 * @author T.OTA
051 * @sinse JDK7.0
052 */
053public class StorageAPI_bluemix implements StorageAPI {
054
055        //      // クラス変数
056        //      /** コンテナ名 */
057        //      private final String container ;
058        //      /* ユーザ名 */
059        //      private final String username ;
060        //      /** パスワード */
061        //      private final String password ;
062        //      /** ドメインID */
063        //      private final String domainId ;
064        //      /** プロジェクトID */
065        //      private final String projectId ;
066        //      /** 認証URL */
067        //      private final String auth_url ;
068
069        /**
070         * コンストラクタ。
071         *
072         * bluemixに設定されているユーザ情報の取得。
073         * システムIDを名称としたコンテナを作成する。
074         *
075         * @param container コンテナ
076         * @param hsession セッション
077         */
078        public StorageAPI_bluemix( final String container, final HttpSession hsession) {
079/***********
080                // クラス変数に設定
081                this.container = container;
082                // CloudFoundryの環境変数から、接続情報を取得します。
083                String env = System.getenv("VCAP_SERVICES");
084                ObjectMapper mapper = new ObjectMapper();
085                try {
086                        JsonNode node = mapper.readTree(env);
087                        Iterator<JsonNode> userNode = node.get("Object-Storage").elements();
088                        JsonNode cred = (JsonNode) userNode.next().get("credentials");
089
090                        // ユーザ名
091                        username = cred.get("username").textValue();
092                        // パスワード
093                        password = cred.get("password").textValue();
094                        // ドメインID
095                        domainId = cred.get("domainId").textValue();
096                        // プロジェクトID
097                        projectId = cred.get("projectId").textValue();
098                        // 認証url
099                        auth_url = cred.get("auth_url").textValue() + "/v3";
100                } catch (Exception e) {
101                        String errMsg = "VCAP_SERVICESの取得に失敗しました。ストレージと接続されているか確認して下さい。" + e;
102                        throw new HybsSystemException(errMsg);
103                }
104
105                // コンテナの作成(既に存在する場合は、そのまま通過する)
106                try{
107                        ObjectStorageService objectStorage = auth(hsession);
108                        objectStorage.containers().create(container);
109                }catch(Exception e){
110                        StringBuilder sbErrMsg = new StringBuilder();
111                        sbErrMsg.append("コンテナの作成に失敗しました。container:");
112                        sbErrMsg.append(container);
113                        sbErrMsg.append(" errInfo:");
114                        sbErrMsg.append(e);
115                        throw new HybsSystemException(sbErrMsg.toString());
116                }
117*************/
118        }
119
120        /**
121         * 認証処理
122         * @param hsession      セッション
123         * @return ObjectStorageService
124         */
125/***********
126        private ObjectStorageService auth(HttpSession hsession) {
127                OSClientSession<?, ?> session = OSClientSession.getCurrent();
128                if (session != null) {
129                        // 既に認証されている場合は、認証情報を返却
130                        return session.objectStorage();
131                } else {
132                        // セッションから認証トークンを取得
133                        String token = (String) hsession.getAttribute(SESSION_CLOUD_TOKEN);
134                        // 認証トークンがある場合は、トークンによる認証を行う
135                        if (token != null && !"".equals(token)) {
136                                // トークンによる認証
137                                OSClientV3 os = OSFactory.builderV3().endpoint(auth_url).token(token)
138                                                .scopeToProject(Identifier.byId(projectId))
139                                                .authenticate();
140                                return os.objectStorage();
141                        }
142                }
143
144                // ユーザによる認証(スレッド間はOSClientSessionに保持される)
145                Identifier domainIdentifier = Identifier.byId(domainId);
146                OSClientV3 os = OSFactory.builderV3().endpoint(auth_url).credentials(username, password, domainIdentifier)
147                                .scopeToProject(Identifier.byId(projectId))
148                                .authenticate();
149
150                // 認証トークンの保持
151                hsession.setAttribute(SESSION_CLOUD_TOKEN, os.getToken().getId());
152
153                ObjectStorageService objectStorage = os.objectStorage();
154
155                return objectStorage;
156        }
157*************/
158
159        /**
160         * アップロード。
161         *
162         * @param partInputStream       アップロード対象のストリーム
163         * @param updFolder             アップロードフォルタ名
164         * @param updFileName           アップロードファイル名
165         * @param hsession                      セッション
166         */
167        @Override
168        public void add( final InputStream partInputStream, final String updFolder, final String updFileName, final HttpSession hsession) {
169/***********
170                // 認証
171                ObjectStorageService objectStorage = auth(hsession);
172                // アップロードストレーム
173                Payload<InputStream> payload = new InputPayload<InputStream>(partInputStream);
174                try {
175                        // アップロード処理
176                        objectStorage.objects().put(this.container, updFolder + updFileName, payload);
177                } catch (Exception e) {
178                        StringBuilder sbErrMsg = new StringBuilder();
179                        sbErrMsg.append("ストレージへのファイルアップロードに失敗しました。updFolder:");
180                        sbErrMsg.append(updFolder);
181                        sbErrMsg.append(" updFileName:");
182                        sbErrMsg.append(updFileName);
183                        sbErrMsg.append(" errInfo:");
184                        sbErrMsg.append(e);
185                        throw new HybsSystemException(sbErrMsg.toString());
186                } finally {
187                        // クローズ処理
188                        Closer.ioClose(partInputStream);
189                        Closer.ioClose(payload);
190                }
191*************/
192        }
193
194        /**
195         * ダウンロード。
196         *
197         * @param filePath      ダウンロード対象のファイルパス
198         * @param hsession      セッション
199         * @return ストリーム
200         */
201        @Override
202        public InputStream get( final String filePath, final HttpSession hsession) {
203/***********
204                // 認証
205                ObjectStorageService objectStorage = auth(hsession);
206                DLPayload payload = null;
207                // ダウンロード
208                try {
209                        SwiftObject swiftObject = objectStorage.objects().get(ObjectLocation.create(this.container, filePath));
210                        payload = swiftObject.download();
211                } catch (Exception e) {
212                        StringBuilder sbErrMsg = new StringBuilder();
213                        sbErrMsg.append("ストレージからのファイルダウンロードに失敗しました。filePath:");
214                        sbErrMsg.append(filePath);
215                        sbErrMsg.append(" errInfo:");
216                        sbErrMsg.append(e);
217                        throw new HybsSystemException(sbErrMsg.toString());
218                }
219
220                return payload.getInputStream();
221*************/
222                return null;
223        }
224
225        /**
226         * コピー。
227         *
228         * @param oldFilePath   コピー元ファイルパス
229         * @param newFilePath   コピー先ファイルパス
230         * @param hsession              セッション
231         */
232        @Override
233        public void copy( final String oldFilePath, final String newFilePath, final HttpSession hsession) {
234/***********
235                // コピー処理
236                Payload<InputStream> payload = null;
237                InputStream is = null;
238                try {
239                        // openstack4jにcopyメソッドは実装されているが、全角文字が利用できないため、
240                        // ダウンロード・アップロードで対応
241                        // コピー元情報の取得
242                        is = get(oldFilePath, hsession);
243                        // コピー先に登録
244                        payload = new InputPayload<InputStream>(is);
245
246                        // 認証
247                        ObjectStorageService objectStorage = auth(hsession);
248                        objectStorage.objects().put(this.container, newFilePath, payload);
249                } catch (Exception e) {
250                        StringBuilder sbErrMsg = new StringBuilder();
251                        sbErrMsg.append("ストレージのファイルコピー処理に失敗しました。oldFilePath:");
252                        sbErrMsg.append(oldFilePath);
253                        sbErrMsg.append(" newFilePath:");
254                        sbErrMsg.append(newFilePath);
255                        sbErrMsg.append(" errInfo:");
256                        sbErrMsg.append(e);
257                        throw new HybsSystemException(sbErrMsg.toString());
258                }finally{
259                        // クローズ処理
260                        Closer.ioClose(payload);
261                        Closer.ioClose(is);
262                }
263*************/
264        }
265
266        /**
267         * 削除。
268         *
269         * @param filePath      削除ファイルのパス
270         * @param hsession      セッション
271         */
272        @Override
273        public void delete( final String filePath, final HttpSession hsession) {
274/***********
275                // 認証
276                ObjectStorageService objectStorage = auth(hsession);
277                // 削除
278                try {
279                        objectStorage.objects().delete(ObjectLocation.create(this.container, filePath));
280                } catch (Exception e) {
281                        StringBuilder sbErrMsg = new StringBuilder();
282                        sbErrMsg.append("ストレージのファイル削除に失敗しました。filePath:");
283                        sbErrMsg.append(filePath);
284                        sbErrMsg.append(" errInfo:");
285                        sbErrMsg.append(e);
286                        throw new HybsSystemException(sbErrMsg.toString());
287                }
288*************/
289        }
290
291        /**
292         * ファイル名変更。
293         *
294         * @param filePath              ファイルパス
295         * @param oldFileName   変更前ファイル名
296         * @param newFileName   変更後ファイル名
297         * @param useBackup     変更後ファイル名が既に存在する場合のバックアップ作成フラグ
298         * @param hsession              セッション
299         */
300        public void rename( final String filePath, final String oldFileName, final String newFileName, final boolean useBackup,final HttpSession hsession) {
301/***********
302                String newFilePath = filePath + newFileName;
303                String oldFilePath = filePath + oldFileName;
304
305                // 変更先のファイルが存在した場合の処理
306                if (exists(newFilePath, hsession)) {
307                        // バックアップ作成する場合
308                        if (useBackup) {
309                                // バックアップファイル名は、元のファイル名(拡張子含む) + "_" + 現在時刻のlong値 + "." +
310                                // 元のファイルの拡張子
311                                String bkupPath = filePath + "_backup/" + newFileName + "_" + System.currentTimeMillis()
312                                                + FileUtil.EXTENSION_SEPARATOR + FileUtil.getExtension(newFileName);
313                                // バックアップフォルダに移動
314                                copy(newFilePath, bkupPath, hsession);
315                        }
316                }
317
318                // コピー
319                copy(oldFilePath, newFilePath, hsession);
320                // 削除
321                delete(oldFilePath, hsession);
322*************/
323        }
324
325        /**
326         * ファイル存在チェック。
327         *
328         * @param filePath                      ファイルパス
329         * @param hsession              セッション
330         * @return                              true:存在 false:存在しない
331         */
332        @Override
333        public boolean exists( final String filePath, final HttpSession hsession) {
334                boolean blnRtn = true;
335/***********
336
337                // 認証
338                ObjectStorageService objectStorage = auth(hsession);
339
340                try {
341                        SwiftObject so = objectStorage.objects().get(ObjectLocation.create(this.container, filePath));
342
343                        if (so == null) {
344                                // ファイルが取得できなかった場合は、falseを設定
345                                blnRtn = false;
346                        }
347                } catch (Exception e) {
348                        StringBuilder sbErrMsg = new StringBuilder();
349                        sbErrMsg.append("ストレージのファイル取得に失敗しました。filePath:");
350                        sbErrMsg.append(filePath);
351                        sbErrMsg.append(" errInfo:");
352                        sbErrMsg.append(e);
353                        throw new HybsSystemException(sbErrMsg.toString());
354                }
355
356*************/
357                return blnRtn;
358        }
359
360        /**
361         * ファイル一覧取得。
362         *
363         * @param startsWith    パスの前方一致
364         * @param hsession              セッション
365         * @return                              ファイルパス一覧
366         */
367        @Override
368        public String[] list( final String startsWith, final HttpSession hsession) {
369                final List<String> rtnList = new ArrayList<String>();
370/***********
371                // 認証
372                ObjectStorageService objectStorage = auth(hsession);
373                List<? extends SwiftObject> list = null;
374                try{
375                        // オプションの指定
376                        ObjectListOptions olo = ObjectListOptions.create().startsWith(startsWith);
377                        // 一覧の取得
378                        list = objectStorage.objects().list(this.container, olo);
379                        for(SwiftObject so: list){
380                                rtnList.add(so.getName());
381                        }
382                } catch (Exception e){
383                        StringBuilder sbErrMsg = new StringBuilder();
384                        sbErrMsg.append("ファイル一覧の取得に失敗しました。startsWith:");
385                        sbErrMsg.append(startsWith);
386                        sbErrMsg.append(" errInfo:");
387                        sbErrMsg.append("e");
388                        throw new HybsSystemException(sbErrMsg.toString());
389                }
390*************/
391                return rtnList.toArray(new String[rtnList.size()]);
392        }
393
394        /**
395         * ファイル情報取得。
396         *
397         * @param path                  ファイルパス
398         * @param hsession              セッション
399         * @return                              ファイル情報格納Map
400         */
401        @Override
402        public Map<String, String> getInfo( final String path, final HttpSession hsession) {
403                final Map<String, String> rtnMap = new HashMap<String,String>();
404/***********
405
406                // 認証
407                ObjectStorageService objectStorage = auth(hsession);
408
409                SwiftObject so = null;
410                try{
411                        // ファイルオブジェクトの取得
412                         so = objectStorage.objects().get(ObjectLocation.create(this.container, path));
413                }catch(Exception e){
414                        StringBuilder sbErrMsg = new StringBuilder();
415                        sbErrMsg.append("ファイルの取得に失敗しました。path:");
416                        sbErrMsg.append(path);
417                        sbErrMsg.append(" errInfo:");
418                        sbErrMsg.append(e);
419                        throw new HybsSystemException(sbErrMsg.toString());
420                }
421                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
422
423                // ファイルサイズ
424                rtnMap.put(FILEINFO_SIZE, String.valueOf(so.getSizeInBytes()));
425                // 最終更新時刻
426                rtnMap.put(FILEINFO_LASTMODIFIED, sdf.format(so.getLastModified()));
427
428*************/
429                return rtnMap;
430        }
431
432        
433        /**
434         * payloadを利用するための内部クラス。
435         * (AWSやAzureで利用するか不明なので、とりあえず内部クラスとしておきます)
436         *
437         * @param <T>
438         */
439        //      public class InputPayload<T extends InputStream> implements Payload<T>{
440        //              private T stream = null;
441        //      
442        //              /**
443        //               * @param stream
444        //               */
445        //              public InputPayload(T stream) {
446        //                      this.stream = stream;
447        //              }
448        //      
449        //              @Override
450        //              public void close() throws IOException {
451        //                      stream.close();
452        //              }
453        //      
454        //              @Override
455        //              public T open() {
456        //                      return stream;
457        //              }
458        //      
459        //              @Override
460        //              public void closeQuietly() {
461        //                      Closer.ioClose(stream);
462        //              }
463        //      
464        //              @Override
465        //              public T getRaw() {
466        //                      return stream;
467        //              }
468        //      }
469}