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