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