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.2.0 (2021/11/30)" ; 053 private static final long serialVersionUID = 802020211130L ; 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 * @og.rev 8.0.2.0 (2021/11/30) fukurou.util.rTrim(String,char) 使用 469 * 470 * @param path ファイルパス 471 * @return 変更後パス 472 */ 473// protected String editPath(final String path) { 474 private String editPath(final String path) { 475 if (StringUtil.isNull(path)) { 476 return ""; 477 } 478 String rtn = path; 479 480 // 「//+」は「/」に置換 481 rtn = rtn.replaceAll("//+", "/"); 482 // 先頭が「/」の場合は除去 483// if ("/".equals(rtn.substring(0, 1))) { 484 if( FS == rtn.charAt(0) ) { 485 rtn = rtn.substring(1); 486 } 487 // 後尾の「.」は除去 488// rtn = rTrim(rtn, '.'); 489 rtn = StringUtil.rTrim(rtn, '.'); // 8.0.2.0 (2021/11/30) 490 // 後尾の「/」は除去 491// rtn = rTrim(rtn, FS); 492 rtn = StringUtil.rTrim(rtn, FS); // 8.0.2.0 (2021/11/30) 493 494 // 5.10.12.2 (2019/06/17) 495 // 「../」の文字列は1つ上のディレクトリに変換を行います。 496 Matcher mtc = PTN_PRE_DIR.matcher(rtn); 497 498 // 「../」が無くなるまで、1つずづ変換します。 499 while(mtc.find()) { 500 rtn = mtc.replaceFirst(""); 501 mtc = PTN_PRE_DIR.matcher(rtn); 502 } 503 504 return rtn; 505 } 506 507 /** 508 * 親のパスを抽出 509 * 510 * キーから親のパスを抽出します。 511 * 512 * @og.rev 8.0.0.1 (2021/10/08) protected → private 513 * 514 * @param key キー 515 * @return 親のパス 516 */ 517// protected String drawParent(final String key) { 518 private String drawParent(final String key) { 519 final int k = key.lastIndexOf(FS); 520 521 String rtn = ""; 522 if (k > 0) { 523 rtn = key.substring(0, key.lastIndexOf(FS)); 524 } 525 if ("/".equals(File.separator)) { 526 rtn = File.separator + rtn; 527 } 528 529 return rtn; 530 } 531 532 /** 533 * 名称の抽出 534 * 535 * 引数のkeyから名称を抽出します。 536 * 537 * @og.rev 8.0.0.1 (2021/10/08) protected → private 538 * 539 * @param key キー(パス) 540 * @return 名称 541 */ 542// protected String drawName(final String key) { 543 private String drawName(final String key) { 544 final int k = key.lastIndexOf(FS); 545 546 String rtn = key; 547 if (k > 0) { 548 rtn = key.substring(key.lastIndexOf(FS) + 1); 549 } 550 return rtn; 551 } 552 553 /** 554 * ディレクトリ用のパス編集 555 * 556 * 後尾に「/」がない場合は、付与します。 557 * 558 * @param path パス 559 * @return 後尾に「/」ありのパス 560 */ 561 protected String setDirTail(final String path) { 562 if (StringUtil.isNull(path)) { 563 return path; 564 } 565 566 final StringBuilder sb = new StringBuilder(path); 567// if (!"/".equals(path.substring(path.length() - 1))) { 568 if ( FS != path.charAt(path.length() - 1) ) { 569 sb.append(FS); 570 } 571 return sb.toString(); 572 } 573 574// /** 575// * 右側トリム処理 576// * 577// * @og.rev 8.0.2.0 (2021/11/30) fukurou.util.rTrim(String,char) 使用 578// * 579// * 右側の文字が、指定の文字の場合、除去します。 580// * 581// * @param str 対象文字列 582// * @param chr 指定文字 583// * @return 右側から指定文字を除去後の文字列 584// */ 585// protected String rTrim(final String str, final char chr) { 586// String rtn = str; 587// int trgPos = 0; 588// for( int i = str.length() - 1; i >= 0; i--) { 589// if (str.charAt(i) == chr) { 590// trgPos = i; 591// // すべて合致した場合は、から文字を返す 592// if (trgPos == 0) { 593// rtn = ""; 594// } 595// } else { 596// break; 597// } 598// } 599// 600// if (trgPos > 0) { 601// rtn = str.substring(0, trgPos); 602// } 603// 604// return rtn; 605// } 606 607 /** 608 * ファイル区切り文字変換 609 * 610 * ファイル区切り文字を変換します。 611 * 612 * @og.rev 8.0.0.1 (2021/10/08) protected → private 613 * 614 * @param path 変換前文字列 615 * @return 返還後文字列 616 */ 617// protected String replaceFileSeparetor(final String path) { 618 private String replaceFileSeparetor(final String path) { 619 if (StringUtil.isNull(path)) { 620 return ""; 621 } 622 623 return path.replaceAll("\\\\", "/"); 624 } 625 626 /** 627 * フィルター処理 628 * 629 * フィルター処理を行います。 630 * 631 * @param list フィルタを行うリスト 632 * @param filter フィルタ情報 633 * @return フィルタ後のリスト 634 */ 635 protected File[] filter(final List<File> list, final FileFilter filter) { 636 final List<File> files = new ArrayList<File>(); 637 for( final File file : list ) { 638 if (filter.accept(file)) { 639 files.add(file); 640 } 641 } 642 return files.toArray(new File[files.size()]); 643 } 644 645 /** 646 * ストリームの変換処理 647 * 648 * InputStreamをbyte[]に変換。 649 * InputStreamのサイズ計算に利用。 650 * 651 * @param is byte配列変換するInputStream 652 * @return InpusStreamをbyte配列に変換した値 653 * @throws IOException ファイル関連エラー情報 654 */ 655 protected byte[] toByteArray(final InputStream is) throws IOException { 656 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 657 try { 658 // 7.2.9.4 (2020/11/20) Avoid variables with short names like b b → bt , n → no 659 final byte[] bt = new byte[BUFFER_SIZE]; 660 int no = 0; 661 while ((no = is.read(bt)) != -1) { 662 output.write(bt, 0, no); 663 } 664 return output.toByteArray(); 665 } finally { 666 output.close(); 667 } 668 } 669 670// /** 671// * ローカル実行フラグ判定 672// * 673// * このabstract クラスの継承クラスはクラウド上で実行されるため、 674// * falseを返します。 675// * 676// * @return ローカル実行フラグ 677// */ 678// @Override 679// public boolean isLocal() { 680// return false; 681// } 682 683 /** 684 * 保存先のクラウド判定。 685 * 686 * 判定結果を返します。 687 * trueの場合は、クラウドストレージ保存。 688 * falseの場合は、ローカルに保存です。 689 * 690 * @return クラウドならtrue 691 */ 692 public boolean isCloud() { 693 return true; 694 } 695 696 /** java.io.Fileに実装されており、クラウド用ファイルクラスに未実装のメソッドの対応 */ 697 698 /** 699 * canExecuteの実行(Fileクラス) 700 * 701 * クラウド側では未実装のメソッドです。 702 * 703 * @return フラグ 704 * @see java.io.File#canExecute() 705 */ 706 @Override 707 public boolean canExecute() { 708 throw new RuntimeException(UNIMPLEMNTED_ERR); 709 } 710 711 /** 712 * deleteOnExitの実行(Fileクラス) 713 * 714 * クラウド側では未実装のメソッドです。 715 * 716 * @see java.io.File#deleteOnExit() 717 */ 718 @Override 719 public void deleteOnExit() { 720 throw new RuntimeException(UNIMPLEMNTED_ERR); 721 } 722 723 /** 724 * getAbsoluteFileの実行(Fileクラス) 725 * 726 * クラウド側では未実装のメソッドです。 727 * 728 * @return Fileオブジェクト 729 * @see java.io.File#getAbsoluteFile() 730 */ 731 @Override 732 public File getAbsoluteFile() { 733 throw new RuntimeException(UNIMPLEMNTED_ERR); 734 } 735 736 /** 737 * getFreeSpaceの実行(Fileクラス) 738 * 739 * クラウド側では未実装のメソッドです。 740 * 741 * @return 数値 742 * @see java.io.File#getFreeSpace() 743 */ 744 @Override 745 public long getFreeSpace() { 746 throw new RuntimeException(UNIMPLEMNTED_ERR); 747 } 748 749 /** 750 * getTotalSpaceの実行(Fileクラス) 751 * 752 * クラウド側では未実装のメソッドです。 753 * 754 * @return 数値 755 * @see java.io.File#getTotalSpace() 756 */ 757 @Override 758 public long getTotalSpace() { 759 throw new RuntimeException(UNIMPLEMNTED_ERR); 760 } 761 762 /** 763 * getUsableSpaceの実行(Fileクラス) 764 * 765 * クラウド側では未実装のメソッドです。 766 * 767 * @return 数値 768 * @see java.io.File#getUsableSpace() 769 */ 770 @Override 771 public long getUsableSpace() { 772 throw new RuntimeException(UNIMPLEMNTED_ERR); 773 } 774 775 /** 776 * isAbsoluteの実行(Fileクラス) 777 * 778 * クラウド側では未実装のメソッドです。 779 * 780 * @return フラグ 781 * @see java.io.File#isAbsolute() 782 */ 783 @Override 784 public boolean isAbsolute() { 785 throw new RuntimeException(UNIMPLEMNTED_ERR); 786 } 787 788 /** 789 * setReadableの実行(Fileクラス) 790 * 791 * クラウド側では未実装のメソッドです。 792 * 793 * @param readable フラグ 794 * @return フラグ 795 * @see java.io.File#setReadable(boolean) 796 */ 797 @Override 798 public boolean setReadable(final boolean readable) { 799 throw new RuntimeException(UNIMPLEMNTED_ERR); 800 } 801 802 /** 803 * setReadableの実行(Fileクラス) 804 * 805 * クラウド側では未実装のメソッドです。 806 * 807 * @param readable フラグ 808 * @param ownerOnly フラグ 809 * @return フラグ 810 * @see java.io.File#setReadable(boolean,boolean) 811 */ 812 @Override 813 public boolean setReadable(final boolean readable, final boolean ownerOnly) { 814 throw new RuntimeException(UNIMPLEMNTED_ERR); 815 } 816 817 /** 818 * setWritableの実行(Fileクラス) 819 * 820 * クラウド側では未実装のメソッドです。 821 * 822 * @param writable フラグ 823 * @return フラグ 824 * @see java.io.File#setWritable(boolean) 825 */ 826 @Override 827 public boolean setWritable(final boolean writable) { 828 throw new RuntimeException(UNIMPLEMNTED_ERR); 829 } 830 831 /** 832 * canExecuteの実行(Fileクラス) 833 * 834 * クラウド側では未実装のメソッドです。 835 * 836 * @param writable フラグ 837 * @param ownerOnly フラグ 838 * @return フラグ 839 * @see java.io.File#setWritable(boolean,boolean) 840 */ 841 @Override 842 public boolean setWritable(final boolean writable, final boolean ownerOnly) { 843 throw new RuntimeException(UNIMPLEMNTED_ERR); 844 } 845 846 /** 847 * canExecuteの実行(Fileクラス) 848 * 849 * クラウド側では未実装のメソッドです。 850 * 851 * @return URI情報 852 * @see java.io.File#toURI() 853 */ 854 @Override 855 public URI toURI() { 856 throw new RuntimeException(UNIMPLEMNTED_ERR); 857 } 858}