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