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