001package org.opengion.fukurou.model; 002 003import java.io.ByteArrayInputStream; 004import java.io.ByteArrayOutputStream; 005import java.io.File; 006import java.io.FileFilter; 007import java.io.FileNotFoundException; 008import java.io.IOException; 009import java.io.InputStream; 010import java.net.URI; 011import java.util.ArrayList; 012import java.util.List; 013 014import org.opengion.fukurou.util.Closer; 015import org.opengion.fukurou.util.StringUtil; 016 017/** 018 * クラウドストレージ対応用の抽象クラスです。 019 * 各ベンダーのストレージに対応したプラグインを作成する場合はこのクラスを継承してください。 020 * 021 * 022 * @og.group ファイル操作 023 * 024 * @og.rev 5.10.8.0 (2019/02/01) 新規作成 025 * @og.rev 5.10.9.0 (2019/03/01) 変更対応 026 * @author oota 027 * @since JDK7.0 028 */ 029public abstract class CloudFileOperation extends FileOperation { 030 031 /* クラス定数 */ 032 private static final int BUFFER_SIZE = 1024 * 4; 033 /* クラス変数 */ 034 // パス 035 protected final String conPath; 036 // バケット名 037 protected final String conBucket; 038 039 private final String UNIMPLEMNTED_ERR="このクラスでは未実装のメソッドです。"; 040 041 /** 042 * コンストラクタ 043 * 044 * 045 * @param bucket バケット名 046 * @param inPath ファイルパス 047 */ 048 public CloudFileOperation(final String bucket, final String inPath) { 049 super(inPath); 050 051 this.conPath = editPath(replaceFileSeparetor(inPath)); 052 053 this.conBucket = bucket; 054 055 if (StringUtil.isNull(conBucket)) { 056 String errMsg = "バケット未指定です。hayabusa利用ではシステム変数の「CLOUD_BUCKET」にバケット名を設定して下さい。"; 057 throw new RuntimeException(errMsg); 058 } 059 } 060 061 /** 062 * データ書き込み 063 * 064 * InputStreamのデータを書き込みます。 065 * 066 * @param is 書き込みデータのInputStream 067 * @throws IOException IO関連のエラー情報 068 */ 069 @Override 070 public abstract void write(InputStream is) throws IOException; 071 072 /** 073 * データ読み込み 074 * 075 * データを読み込み、InputStreamを返します。 076 * 077 * @return 読み込みデータのInputStream 078 * @throws FileNotFoundException ファイル非存在エラー情報 079 */ 080 @Override 081 public abstract InputStream read() throws FileNotFoundException; 082 083 /** 084 * ファイル削除 085 * 086 * ファイルを削除します。 087 * 088 * @return 成否フラグ 089 */ 090 @Override 091 public abstract boolean delete(); 092 093 /** 094 * ファイルコピー 095 * 096 * ファイルを指定先にコピーします。 097 * 098 * @param afPath コピー先 099 * @return 成否フラグ 100 */ 101 @Override 102 public abstract boolean copy(String afPath); 103 104 /** 105 * ファイルサイズ取得 106 * 107 * ファイルサイズを返します。 108 * 109 * @return ファイルサイズ 110 */ 111 @Override 112 public abstract long length(); 113 114 /** 115 * 最終更新時刻取得 116 * 117 * 最終更新時刻を返します。 118 * 119 * @return 最終更新時刻 120 */ 121 @Override 122 public abstract long lastModified(); 123 124 /** 125 * ファイル判定 126 * 127 * ファイルの場合は、trueを返します。 128 * 129 * @return ファイルフラグ 130 */ 131 @Override 132 public abstract boolean isFile(); 133 134 /** 135 * ディレクトリ判定 136 * 137 * ディレクトリの場合は、trueを返します。 138 * 139 * @return ディレクトリフラグ 140 */ 141 @Override 142 public abstract boolean isDirectory(); 143 144 /** 145 * 一覧取得 146 * 147 * パスのファイルと、ディレクトリ一覧を取得します。 148 * 149 * @return ファイルとティレクトリ一覧 150 */ 151 public abstract File[] listFiles(FileFilter filter); 152 153 /** 154 * 親ディレクトリの取得 155 * 156 * 親のディレクトリ情報を返します。 157 * 158 * @return 親のディレクトリ 159 */ 160 @Override 161 public abstract File getParentFile(); 162 163 /** 164 * ファイルパス取得 165 * 166 * ファイルパスを取得します。 167 * 168 * @return 設定パス 169 */ 170 @Override 171 public String getPath() { 172 return conPath; 173 } 174 175 /** 176 * 絶対パス取得 177 * 178 * 絶対パスを取得します。 179 * 180 * @return 絶対パス 181 */ 182 @Override 183 public String getAbsolutePath() { 184 return conPath; 185 } 186 187 /** 188 * ファイル名取得 189 * 190 * ファイル名を取得します。 191 * 192 * @return 名称 193 */ 194 @Override 195 public String getName() { 196 return drawName(conPath); 197 } 198 199 /** 200 * 親のパス取得 201 * 202 * 親のパスを取得します。 203 * 204 * @return 親のパス 205 */ 206 @Override 207 public String getParent() { 208 return drawParent(conPath); 209 } 210 211 /** 212 * ファイル移動 213 * 214 * ファイルを指定先に移動します。 215 * 216 * @param afPath 移動先 217 * @return 成否フラグ 218 */ 219 @Override 220 public boolean move(String afPath) { 221 boolean flgRtn = false; 222 223 flgRtn = copy(afPath); 224 if (flgRtn) { 225 flgRtn = delete(); 226 } 227 228 return flgRtn; 229 } 230 231 /** 232 * 存在チェック 233 * 234 * 存在する場合は、trueを返します。 235 * 236 * @return 存在フラグ 237 */ 238 @Override 239 public boolean exists() { 240 return isDirectory() | isFile(); 241 } 242 243 /** 244 * ディレクトリの作成 245 * 246 * ※1つのディレクトリのみ作成します。 247 * クラウドストレージにはディレクトリの概念が無いため、 248 * 作成は行わず、trueを返します。 249 * 250 * @return 成否フラグ 251 */ 252 @Override 253 public boolean mkdir() { 254 return true; 255 } 256 257 /** 258 * ディレクトリの作成(複数) 259 * 260 * ※複数のディレクトリを作成します。 261 * クラウドストレージにはディレクトリの概念が無いため、 262 * 作成は行わず、trueを返します。 263 * 264 * 265 * @return 成否フラグ 266 */ 267 @Override 268 public boolean mkdirs() { 269 return true; 270 } 271 272 /** 273 * ファイル名変更 274 * 275 * 指定のファイル情報のファイル名に変更します。 276 * 277 * @param dest 変更後のファイル情報 278 * @return 成否フラグ 279 */ 280 @Override 281 public boolean renameTo(File dest) { 282 return move(dest.getPath()); 283 } 284 285 /** 286 * 書き込み可能フラグ 287 * 288 * ※クラウドストレージの場合は、 289 * 存在すればtrueを返します。 290 * 291 * @return 書き込み可能フラグ 292 */ 293 @Override 294 public boolean canWrite() { 295 return exists(); 296 } 297 298 /** 299 * 読み取り可能フラグ 300 * 301 * ※クラウドストレージの場合は、 302 * 存在すればtrueを返します。 303 * 304 * @return 読み取り可能フラグ 305 */ 306 @Override 307 public boolean canRead() { 308 return exists(); 309 } 310 311 /** 312 * 隠しファイルフラグ 313 * 314 * ※クラウドストレージの場合は、 315 * 必ずfalseを返します。 316 * 317 * @return 隠しファイルフラグ 318 */ 319 @Override 320 public boolean isHidden() { 321 return false; 322 } 323 324 /** 325 * 新規ファイル作成 326 * 327 * 既にファイルが存在しない場合のみ、 328 * 空のファイルを作成します。 329 * 330 * @return 成否フラグ 331 * @throws IOException ファイル関連エラー情報 332 */ 333 @Override 334 public boolean createNewFile() throws IOException { 335 boolean rtn = false; 336 337 if (!exists()) { 338 InputStream is = null; 339 try { 340 is = new ByteArrayInputStream(new byte[0]); 341 write(is); 342 rtn = true; 343 } finally { 344 Closer.ioClose(is); 345 } 346 } 347 348 return rtn; 349 } 350 351 /** 352 * 最終更新時刻の更新 353 * 354 * 最終更新時刻の更新を行います。 355 * ※クラウドストレージの場合は、 356 * 最終更新時刻の更新を行えません。 357 * 358 * @param time 更新する最終更新時刻 359 * @return 成否フラグ 360 */ 361 @Override 362 public boolean setLastModified(long time) { 363 // クラウドストレージでは、setLastModifiedによる、 364 // 最終更新時刻の設定はできないので、 365 // 処理を行わずにtrueを返します。 366 return true; 367 } 368 369 /** 370 * カノニカルファイル情報の取得 371 * 372 * ※ローカルサーバのみ通常ファイルと、 373 * カノニカルファイルで異なります。 374 * 375 * @return カノニカルファイル情報 376 * @throws IOException ファイル関連エラー情報 377 */ 378 @Override 379 public FileOperation getCanonicalFile() throws IOException { 380 return this; 381 } 382 383 /** 384 * toString 385 * 386 * パスを返します。 387 * 388 * @return ファイルパス 389 */ 390 @Override 391 public String toString() { 392 return conPath; 393 } 394 395 /** 共通関数 **/ 396 /** 397 * ファイルパスの編集 398 * 399 * パスの先頭が「/」の場合は「/」の除去と、「//」を「/」に置換処理の追加。 400 * 401 * @param path ファイルパス 402 * @return 変更後パス 403 */ 404 protected String editPath(final String path) { 405 if (StringUtil.isNull(path)) { 406 return ""; 407 } 408 String rtn = path; 409 410 // 「//+」は「/」に置換 411 rtn = rtn.replaceAll("//+", "/"); 412 // 先頭が「/」の場合は除去 413 if ("/".equals(rtn.substring(0, 1))) { 414 rtn = rtn.substring(1); 415 } 416 // 後尾の「.」は除去 417 rtn = rTrim(rtn, '.'); 418 // 後尾の「/」は除去 419 rtn = rTrim(rtn, '/'); 420 421 return rtn; 422 } 423 424 /** 425 * 親のパスを抽出 426 * 427 * キーから親のパスを抽出します。 428 * 429 * @param key キー 430 * @return 親のパス 431 */ 432 protected String drawParent(String key) { 433 int k = key.lastIndexOf("/"); 434 435 String rtn = ""; 436 if (k > 0) { 437 rtn = key.substring(0, key.lastIndexOf("/")); 438 } 439 if ("/".equals(File.separator)) { 440 rtn = File.separator + rtn; 441 } 442 443 return rtn; 444 } 445 446 /** 447 * 名称の抽出 448 * 449 * 引数のkeyから名称を抽出します。 450 * 451 * @param key キー(パス) 452 * @return 名称 453 */ 454 protected String drawName(String key) { 455 int k = key.lastIndexOf("/"); 456 457 String rtn = key; 458 if (k > 0) { 459 rtn = key.substring(key.lastIndexOf("/") + 1); 460 } 461 return rtn; 462 } 463 464 /** 465 * ディレクトリ用のパス編集 466 * 467 * 後尾に「/」がない場合は、付与します。 468 * 469 * @param path パス 470 * @return 後尾に「/」ありのパス 471 */ 472 protected String setDirTail(String path) { 473 if (StringUtil.isNull(path)) { 474 return path; 475 } 476 477 StringBuilder sb = new StringBuilder(path); 478 if (!"/".equals(path.substring(path.length() - 1))) { 479 sb.append("/"); 480 } 481 return sb.toString(); 482 } 483 484 /** 485 * 右側トリム処理 486 * 487 * 右側の文字が、指定の文字の場合、除去します。 488 * 489 * @param str 対象文字列 490 * @param chr 指定文字 491 * @return 右側から指定文字を除去後の文字列 492 */ 493 protected String rTrim(final String str, char chr) { 494 String rtn = str; 495 int trgPos = 0; 496 for (int i = str.length() - 1; i >= 0; i--) { 497 if (str.charAt(i) == chr) { 498 trgPos = i; 499 // すべて合致した場合は、から文字を返す 500 if (trgPos == 0) { 501 rtn = ""; 502 } 503 } else { 504 break; 505 } 506 } 507 508 if (trgPos > 0) { 509 rtn = str.substring(0, trgPos); 510 } 511 512 return rtn; 513 } 514 515 /** 516 * ファイル区切り文字変換 517 * 518 * ファイル区切り文字を変換します。 519 * 520 * @param path 変換前文字列 521 * @return 返還後文字列 522 */ 523 protected String replaceFileSeparetor(final String path) { 524 if (StringUtil.isNull(path)) { 525 return ""; 526 } 527 528 return path.replaceAll("\\\\", "/"); 529 } 530 531 /** 532 * フィルター処理 533 * 534 * フィルター処理を行います。 535 * 536 * @param list フィルタを行うリスト 537 * @param filter フィルタ情報 538 * @return フィルタ後のリスト 539 */ 540 protected File[] filter(List<File> list, FileFilter filter) { 541 List<File> files = new ArrayList<File>(); 542 for (File file : list) { 543 if (filter.accept(file)) { 544 files.add(file); 545 } 546 } 547 return files.toArray(new File[files.size()]); 548 } 549 550 /** 551 * ストリームの変換処理 552 * 553 * InputStreamをbyte[]に変換。 554 * InputStreamのサイズ計算に利用。 555 * 556 * @param is byte[]変換するInputStream 557 * @return InpusStreamをbyte[]に変換した値 558 * @throws IOException ファイル関連エラー情報 559 */ 560 protected byte[] toByteArray(InputStream is) throws IOException { 561 ByteArrayOutputStream output = new ByteArrayOutputStream(); 562 try { 563 byte[] b = new byte[BUFFER_SIZE]; 564 int n = 0; 565 while ((n = is.read(b)) != -1) { 566 output.write(b, 0, n); 567 } 568 return output.toByteArray(); 569 } finally { 570 output.close(); 571 } 572 } 573 574 /** 575 * ローカル実行フラグ判定 576 * 577 * このabstract クラスの継承クラスはクラウド上で実行されるため、 578 * falseを返します。 579 * 580 * @return ローカル実行フラグ 581 */ 582 @Override 583 public boolean isLocal() { 584 return false; 585 } 586 587 /** java.io.Fileに実装されており、クラウド用ファイルクラスに未実装のメソッドの対応 */ 588 /** 589 * canExecuteの実行 590 * 591 * クラウド側では未実装のメソッドです。 592 * 593 * @return フラグ 594 */ 595 @Override 596 public boolean canExecute() { 597 throw new RuntimeException(UNIMPLEMNTED_ERR); 598 } 599 600 /** 601 * deleteOnExitの実行 602 * 603 * クラウド側では未実装のメソッドです。 604 * 605 */ 606 @Override 607 public void deleteOnExit() { 608 throw new RuntimeException(UNIMPLEMNTED_ERR); 609 } 610 611 /** 612 * getAbsoluteFileの実行 613 * 614 * クラウド側では未実装のメソッドです。 615 * 616 * @return File 617 */ 618 @Override 619 public File getAbsoluteFile() { 620 throw new RuntimeException(UNIMPLEMNTED_ERR); 621 } 622 623 /** 624 * getFreeSpaceの実行 625 * 626 * クラウド側では未実装のメソッドです。 627 * 628 * @return 数値 629 */ 630 @Override 631 public long getFreeSpace() { 632 throw new RuntimeException(UNIMPLEMNTED_ERR); 633 } 634 635 /** 636 * getTotalSpaceの実行 637 * 638 * クラウド側では未実装のメソッドです。 639 * 640 * @return 数値 641 */ 642 @Override 643 public long getTotalSpace() { 644 throw new RuntimeException(UNIMPLEMNTED_ERR); 645 } 646 647 /** 648 * getUsableSpaceの実行 649 * 650 * クラウド側では未実装のメソッドです。 651 * 652 * @return 数値 653 */ 654 @Override 655 public long getUsableSpace() { 656 throw new RuntimeException(UNIMPLEMNTED_ERR); 657 } 658 659 /** 660 * isAbsoluteの実行 661 * 662 * クラウド側では未実装のメソッドです。 663 * 664 * @return フラグ 665 */ 666 @Override 667 public boolean isAbsolute() { 668 throw new RuntimeException(UNIMPLEMNTED_ERR); 669 } 670 671 /** 672 * setReadableの実行 673 * 674 * クラウド側では未実装のメソッドです。 675 * 676 * @param readable フラグ 677 * @return フラグ 678 */ 679 @Override 680 public boolean setReadable(boolean readable) { 681 throw new RuntimeException(UNIMPLEMNTED_ERR); 682 } 683 684 /** 685 * setReadableの実行 686 * 687 * クラウド側では未実装のメソッドです。 688 * 689 * @param readable フラグ 690 * @param ownerOnly フラグ 691 * @return フラグ 692 */ 693 @Override 694 public boolean setReadable(boolean readable, boolean ownerOnly) { 695 throw new RuntimeException(UNIMPLEMNTED_ERR); 696 } 697 698 /** 699 * setWritableの実行 700 * 701 * クラウド側では未実装のメソッドです。 702 * 703 * @param writable フラグ 704 * @return フラグ 705 */ 706 @Override 707 public boolean setWritable(boolean writable) { 708 throw new RuntimeException(UNIMPLEMNTED_ERR); 709 } 710 711 /** 712 * canExecuteの実行 713 * 714 * クラウド側では未実装のメソッドです。 715 * 716 * @param writable フラグ 717 * @param ownerOnly フラグ 718 * @return フラグ 719 */ 720 @Override 721 public boolean setWritable(boolean writable, boolean ownerOnly) { 722 throw new RuntimeException(UNIMPLEMNTED_ERR); 723 } 724 725 /** 726 * canExecuteの実行 727 * 728 * クラウド側では未実装のメソッドです。 729 * 730 * @return URI情報 731 */ 732 @Override 733 public URI toURI() { 734 throw new RuntimeException(UNIMPLEMNTED_ERR); 735 } 736}