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