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}