001 package railo.commons.io.res.type.file; 002 import java.io.BufferedInputStream; 003 import java.io.BufferedOutputStream; 004 import java.io.File; 005 import java.io.FileInputStream; 006 import java.io.FileNotFoundException; 007 import java.io.FileOutputStream; 008 import java.io.IOException; 009 import java.io.InputStream; 010 import java.io.OutputStream; 011 import java.util.ArrayList; 012 import java.util.List; 013 014 import railo.commons.cli.Command; 015 import railo.commons.io.IOUtil; 016 import railo.commons.io.ModeUtil; 017 import railo.commons.io.SystemUtil; 018 import railo.commons.io.res.ContentType; 019 import railo.commons.io.res.Resource; 020 import railo.commons.io.res.ResourceProvider; 021 import railo.commons.io.res.filter.ResourceFilter; 022 import railo.commons.io.res.filter.ResourceNameFilter; 023 import railo.commons.io.res.util.ResourceOutputStream; 024 import railo.commons.io.res.util.ResourceUtil; 025 026 /** 027 * Implementation og Resource for the local filesystem (java.io.File) 028 */ 029 public final class FileResource extends File implements Resource { 030 031 private final FileResourceProvider provider; 032 033 /** 034 * Constructor for the factory 035 * @param pathname 036 */ 037 FileResource(FileResourceProvider provider,String pathname) { 038 super(pathname); 039 this.provider=provider; 040 } 041 042 /** 043 * Inner Constr constructor to create parent/child 044 * @param parent 045 * @param child 046 */ 047 private FileResource(FileResourceProvider provider,File parent, String child) { 048 super(parent, child); 049 this.provider=provider; 050 } 051 052 053 /** 054 * @see railo.commons.io.res.Resource#copyFrom(railo.commons.io.res.Resource,boolean) 055 */ 056 public void copyFrom(Resource res,boolean append) throws IOException { 057 IOUtil.copy(res, this.getOutputStream(append),true); 058 } 059 060 /** 061 * @see railo.commons.io.res.Resource#copyTo(railo.commons.io.res.Resource,boolean) 062 */ 063 public void copyTo(Resource res,boolean append) throws IOException { 064 IOUtil.copy(this, res.getOutputStream(append),true); 065 } 066 067 /** 068 * @see Resource#getAbsoluteResource() 069 */ 070 public Resource getAbsoluteResource() { 071 return new FileResource(provider,getAbsolutePath()); 072 } 073 074 /** 075 * @see Resource#getCanonicalResource() 076 */ 077 public Resource getCanonicalResource() throws IOException { 078 return new FileResource(provider,getCanonicalPath()); 079 } 080 081 /** 082 * @see Resource#getParentResource() 083 */ 084 public Resource getParentResource() { 085 String p = getParent(); 086 if(p==null) return null; 087 return new FileResource(provider,p); 088 } 089 090 /** 091 * @see Resource#listResources() 092 */ 093 public Resource[] listResources() { 094 String[] files = list(); 095 if(files==null) return null; 096 097 Resource[] resources=new Resource[files.length]; 098 for(int i=0;i<files.length;i++) { 099 resources[i]=getRealResource(files[i]); 100 } 101 return resources; 102 } 103 104 /** 105 * @see res.Resource#list(res.filter.ResourceFilter) 106 */ 107 public String[] list(ResourceFilter filter) { 108 String[] files = list(); 109 if(files==null) return null; 110 111 List list=new ArrayList(); 112 FileResource res; 113 for(int i=0;i<files.length;i++) { 114 res=new FileResource(provider,this,files[i]); 115 if(filter.accept(res))list.add(files[i]); 116 } 117 return (String[]) list.toArray(new String[list.size()]); 118 } 119 120 /** 121 * @see res.Resource#listResources(res.filter.ResourceFilter) 122 */ 123 public Resource[] listResources(ResourceFilter filter) { 124 String[] files = list(); 125 if(files==null) return null; 126 127 List list=new ArrayList(); 128 Resource res; 129 for(int i=0;i<files.length;i++) { 130 res=getRealResource(files[i]); 131 if(filter.accept(res))list.add(res); 132 } 133 return (Resource[]) list.toArray(new FileResource[list.size()]); 134 } 135 136 137 /** 138 * @see res.Resource#list(res.filter.ResourceNameFilter) 139 */ 140 public String[] list(ResourceNameFilter filter) { 141 String[] files = list(); 142 if(files==null) return null; 143 List list=new ArrayList(); 144 for(int i=0;i<files.length;i++) { 145 if(filter.accept(this,files[i]))list.add(files[i]); 146 } 147 return (String[]) list.toArray(new String[list.size()]); 148 } 149 150 /** 151 * @see res.Resource#listResources(res.filter.ResourceNameFilter) 152 */ 153 public Resource[] listResources(ResourceNameFilter filter) { 154 String[] files = list(); 155 if(files==null) return null; 156 157 List list=new ArrayList(); 158 for(int i=0;i<files.length;i++) { 159 if(filter.accept(this,files[i]))list.add(getRealResource(files[i])); 160 } 161 return (Resource[]) list.toArray(new Resource[list.size()]); 162 } 163 164 /** 165 * @see res.Resource#moveTo(res.Resource) 166 */ 167 public void moveTo(Resource dest) throws IOException { 168 if(this.equals(dest)) return; 169 if(dest instanceof File) { 170 provider.lock(this); 171 try { 172 if(dest.exists() && !dest.delete()) 173 throw new IOException("can't move file "+this.getAbsolutePath()+" cannot remove existing file "+dest.getAbsolutePath()); 174 175 if(!super.renameTo((File)dest)) { 176 throw new IOException("can't move file "+this.getAbsolutePath()+" to destination resource "+dest.getAbsolutePath()); 177 } 178 } 179 finally { 180 provider.unlock(this); 181 } 182 } 183 else { 184 ResourceUtil.checkMoveToOK(this, dest); 185 IOUtil.copy(getInputStream(),dest,true); 186 if(!this.delete()) { 187 throw new IOException("can't delete resource "+this.getAbsolutePath()); 188 } 189 } 190 } 191 192 /** 193 * @see res.Resource#getInputStream() 194 */ 195 public InputStream getInputStream() throws IOException { 196 //provider.lock(this); 197 provider.read(this); 198 try { 199 //return new BufferedInputStream(new ResourceInputStream(this,new FileInputStream(this))); 200 return new BufferedInputStream(new FileInputStream(this)); 201 } 202 catch(IOException ioe) { 203 //provider.unlock(this); 204 throw ioe; 205 } 206 } 207 208 /** 209 * @see res.Resource#getOutputStream() 210 */ 211 public OutputStream getOutputStream() throws IOException { 212 return getOutputStream(false); 213 } 214 215 /** 216 * 217 * @throws FileNotFoundException 218 * @see railo.commons.io.res.Resource#getOutputStream(boolean) 219 */ 220 public OutputStream getOutputStream(boolean append) throws IOException { 221 provider.lock(this); 222 try { 223 if(!super.exists() && !super.createNewFile()) { 224 throw new IOException("can't create file "+this); 225 } 226 return new BufferedOutputStream(new ResourceOutputStream(this,new FileOutputStream(this,append))); 227 } 228 catch(IOException ioe) { 229 provider.unlock(this); 230 throw ioe; 231 } 232 } 233 234 /** 235 * @throws IOException 236 * @see res.Resource#createFile(boolean) 237 */ 238 public void createFile(boolean createParentWhenNotExists) throws IOException { 239 provider.lock(this); 240 try { 241 if(createParentWhenNotExists) { 242 File p = super.getParentFile(); 243 if(!p.exists()) p.mkdirs(); 244 } 245 if(!super.createNewFile()) { 246 if(super.isFile()) throw new IOException("can't create file "+this+", file already exists"); 247 throw new IOException("can't create file "+this); 248 } 249 } 250 finally { 251 provider.unlock(this); 252 } 253 } 254 255 /** 256 * @see res.Resource#removeE(boolean) 257 */ 258 public void remove(boolean alsoRemoveChildren) throws IOException { 259 if(alsoRemoveChildren && isDirectory()) { 260 Resource[] children = listResources(); 261 for(int i=0;i<children.length;i++) { 262 children[i].remove(alsoRemoveChildren); 263 } 264 } 265 provider.lock(this); 266 try { 267 if(!super.delete()) { 268 if(!super.exists())throw new IOException("can't delete file "+this+", file does not exists"); 269 if(!super.canWrite())throw new IOException("can't delete file "+this+", no access"); 270 throw new IOException("can't delete file "+this); 271 } 272 } 273 finally { 274 provider.unlock(this); 275 } 276 } 277 278 /** 279 * @see res.Resource#getReal(java.lang.String) 280 */ 281 public String getReal(String realpath) { 282 if(realpath.length()<=2) { 283 if(realpath.length()==0) return getPath(); 284 if(realpath.equals(".")) return getPath(); 285 if(realpath.equals("..")) return getParent(); 286 } 287 return new FileResource(provider,this,realpath).getPath(); 288 } 289 290 /** 291 * @see res.Resource#getRealResource(java.lang.String) 292 */ 293 public Resource getRealResource(String realpath) { 294 if(realpath.length()<=2) { 295 if(realpath.length()==0) return this; 296 if(realpath.equals(".")) return this; 297 if(realpath.equals("..")) return getParentResource(); 298 } 299 return new FileResource(provider,this,realpath); 300 } 301 302 /** 303 * @see res.Resource#getContentType() 304 */ 305 public ContentType getContentType() { 306 return ResourceUtil.getContentType(this); 307 } 308 309 /** 310 * @see res.Resource#createDirectory(boolean) 311 */ 312 public void createDirectory(boolean createParentWhenNotExists) throws IOException { 313 provider.lock(this); 314 try { 315 if(createParentWhenNotExists?!_mkdirs():!super.mkdir()) { 316 if(super.isDirectory()) throw new IOException("can't create directory "+this+", directory already exists"); 317 throw new IOException("can't create directory "+this); 318 } 319 } 320 finally { 321 provider.unlock(this); 322 } 323 } 324 325 /** 326 * @see res.Resource#getResourceProvider() 327 */ 328 public ResourceProvider getResourceProvider() { 329 return provider; 330 } 331 332 /** 333 * @see res.Resource#isReadable() 334 */ 335 public boolean isReadable() { 336 return canRead(); 337 } 338 339 /** 340 * @see res.Resource#isWriteable() 341 */ 342 public boolean isWriteable() { 343 return canWrite(); 344 } 345 346 /** 347 * @see res.Resource#renameTo(res.Resource) 348 */ 349 public boolean renameTo(Resource dest) { 350 try { 351 moveTo(dest); 352 return true; 353 } 354 catch (IOException e) {} 355 return false; 356 } 357 358 /** 359 * 360 * @see railo.commons.io.res.Resource#isArchive() 361 */ 362 public boolean isArchive() { 363 return getAttribute(ATTRIBUTE_ARCHIVE); 364 } 365 366 /** 367 * 368 * @see railo.commons.io.res.Resource#isSystem() 369 */ 370 public boolean isSystem() { 371 return getAttribute(ATTRIBUTE_SYSTEM); 372 } 373 374 /** 375 * @see railo.commons.io.res.Resource#getMode() 376 */ 377 public int getMode() { 378 if(!exists()) return 0; 379 if(SystemUtil.isUnix()) { 380 try { 381 // TODO geht nur fuer file 382 String line = Command.execute("ls -ld "+getPath(),false); 383 384 line=line.trim(); 385 line=line.substring(0,line.indexOf(' ')); 386 //print.ln(getPath()); 387 return ModeUtil.toOctalMode(line); 388 389 } catch (Exception e) {} 390 391 } 392 int mode=SystemUtil.isWindows() && exists() ?0111:0; 393 if(super.canRead())mode+=0444; 394 if(super.canWrite())mode+=0222; 395 return mode; 396 } 397 398 public void setMode(int mode) throws IOException { 399 // TODO unter windows mit setReadable usw. 400 if(!SystemUtil.isUnix()) return; 401 provider.lock(this); 402 try { 403 //print.ln(ModeUtil.toStringMode(mode)); 404 if (Runtime.getRuntime().exec( 405 new String[] { "chmod", ModeUtil.toStringMode(mode), getPath() } ).waitFor() != 0) 406 throw new IOException("chmod "+ModeUtil.toStringMode(mode)+" " + toString() + " failed"); 407 } 408 catch (InterruptedException e) { 409 throw new IOException("Interrupted waiting for chmod " + toString()); 410 } 411 finally { 412 provider.unlock(this); 413 } 414 } 415 416 /** 417 * @see railo.commons.io.res.Resource#setArchive(boolean) 418 */ 419 public void setArchive(boolean value) throws IOException { 420 setAttribute(ATTRIBUTE_ARCHIVE, value); 421 } 422 423 /** 424 * 425 * @see railo.commons.io.res.Resource#setHidden(boolean) 426 */ 427 public void setHidden(boolean value) throws IOException { 428 setAttribute(ATTRIBUTE_HIDDEN, value); 429 } 430 431 /** 432 * 433 * @see railo.commons.io.res.Resource#setSystem(boolean) 434 */ 435 public void setSystem(boolean value) throws IOException { 436 setAttribute(ATTRIBUTE_SYSTEM, value); 437 } 438 439 /** 440 * @throws IOException 441 * @see railo.commons.io.res.Resource#setWritable(boolean) 442 */ 443 444 public boolean setReadable(boolean value) { 445 if(!SystemUtil.isUnix()) return false; 446 try { 447 setMode(ModeUtil.setReadable(getMode(), value)); 448 return true; 449 } 450 catch (IOException e) { 451 return false; 452 } 453 } 454 455 public boolean setWritable(boolean value) { 456 // setReadonly 457 if(!value){ 458 try { 459 provider.lock(this); 460 if(!super.setReadOnly()) 461 throw new IOException("can't set resource read-only"); 462 } 463 catch(IOException ioe){ 464 return false; 465 } 466 finally { 467 provider.unlock(this); 468 } 469 return true; 470 } 471 472 if(SystemUtil.isUnix()) { 473 // need no lock because get/setmode has one 474 try { 475 setMode(ModeUtil.setWritable(getMode(), value)); 476 } 477 catch (IOException e) { 478 return false; 479 } 480 return true; 481 } 482 483 try { 484 provider.lock(this); 485 Runtime.getRuntime().exec("attrib -R " + getAbsolutePath()); 486 } 487 catch(IOException ioe){ 488 return false; 489 } 490 finally { 491 provider.unlock(this); 492 } 493 return true; 494 } 495 496 497 498 499 500 501 /** 502 * 503 * @see java.io.File#createNewFile() 504 */ 505 public boolean createNewFile() { 506 try { 507 provider.lock(this); 508 return super.createNewFile(); 509 } 510 catch (IOException e) { 511 return false; 512 } 513 finally { 514 provider.unlock(this); 515 } 516 } 517 518 /** 519 * @see java.io.File#canRead() 520 */ 521 public boolean canRead() { 522 try { 523 provider.read(this); 524 } catch (IOException e) { 525 return false; 526 } 527 return super.canRead(); 528 } 529 530 /** 531 * @see java.io.File#canWrite() 532 */ 533 public boolean canWrite() { 534 try { 535 provider.read(this); 536 } catch (IOException e) { 537 return false; 538 } 539 return super.canWrite(); 540 } 541 542 /** 543 * @see java.io.File#delete() 544 */ 545 public boolean delete() { 546 try { 547 provider.lock(this); 548 return super.delete(); 549 } 550 catch (IOException e) { 551 return false; 552 } 553 finally { 554 provider.unlock(this); 555 } 556 } 557 558 /** 559 * @see java.io.File#exists() 560 */ 561 public boolean exists() { 562 try { 563 provider.read(this); 564 } catch (IOException e) {} 565 566 return super.exists(); 567 } 568 569 570 571 /** 572 * @see java.io.File#isAbsolute() 573 */ 574 public boolean isAbsolute() { 575 try { 576 provider.read(this); 577 } 578 catch (IOException e) { 579 return false; 580 } 581 return super.isAbsolute(); 582 } 583 584 /** 585 * @see java.io.File#isDirectory() 586 */ 587 public boolean isDirectory() { 588 try { 589 provider.read(this); 590 } catch (IOException e) { 591 return false; 592 } 593 return super.isDirectory(); 594 } 595 596 /** 597 * @see java.io.File#isFile() 598 */ 599 public boolean isFile() { 600 try { 601 provider.read(this); 602 } catch (IOException e) { 603 return false; 604 } 605 return super.isFile(); 606 } 607 608 /** 609 * @see java.io.File#isHidden() 610 */ 611 public boolean isHidden() { 612 try { 613 provider.read(this); 614 } catch (IOException e) { 615 return false; 616 } 617 return super.isHidden(); 618 } 619 620 /** 621 * @see java.io.File#lastModified() 622 */ 623 public long lastModified() { 624 try { 625 provider.read(this); 626 } catch (IOException e) { 627 return 0; 628 } 629 return super.lastModified(); 630 } 631 632 /** 633 * @see java.io.File#length() 634 */ 635 public long length() { 636 try { 637 provider.read(this); 638 } catch (IOException e) { 639 return 0; 640 } 641 return super.length(); 642 } 643 644 /** 645 * @see java.io.File#list() 646 */ 647 public String[] list() { 648 try { 649 provider.read(this); 650 } catch (IOException e) { 651 return null; 652 } 653 return super.list(); 654 } 655 656 /** 657 * @see java.io.File#mkdir() 658 */ 659 public boolean mkdir() { 660 try { 661 provider.lock(this); 662 return super.mkdir(); 663 } 664 catch (IOException e) { 665 return false; 666 } 667 finally { 668 provider.unlock(this); 669 } 670 } 671 672 /** 673 * @see java.io.File#mkdirs() 674 */ 675 public boolean mkdirs() { 676 try { 677 provider.lock(this); 678 return _mkdirs(); 679 680 } 681 catch (IOException e) { 682 return false; 683 } 684 finally { 685 provider.unlock(this); 686 } 687 } 688 689 private boolean _mkdirs() { 690 if (super.exists()) return false; 691 if (super.mkdir()) return true; 692 693 File parent = super.getParentFile(); 694 return (parent != null) && (parent.mkdirs() && super.mkdir()); 695 } 696 697 /** 698 * @see java.io.File#setLastModified(long) 699 */ 700 public boolean setLastModified(long time) { 701 try { 702 provider.lock(this); 703 return super.setLastModified(time); 704 } 705 catch (Throwable t) {// IllegalArgumentException or IOException 706 return false; 707 } 708 finally { 709 provider.unlock(this); 710 } 711 712 } 713 714 /** 715 * 716 * @see java.io.File#setReadOnly() 717 */ 718 public boolean setReadOnly() { 719 try { 720 provider.lock(this); 721 return super.setReadOnly(); 722 } 723 catch (IOException e) { 724 return false; 725 } 726 finally { 727 provider.unlock(this); 728 } 729 } 730 731 public boolean getAttribute(short attribute) { 732 if(!SystemUtil.isWindows()) return false; 733 if(attribute==ATTRIBUTE_HIDDEN) return isHidden(); 734 735 String attr=null; 736 if(attribute==ATTRIBUTE_ARCHIVE) attr="A"; 737 else if(attribute==ATTRIBUTE_SYSTEM) attr="S"; 738 739 try { 740 provider.lock(this); 741 String result = Command.execute("attrib " + getAbsolutePath(),false); 742 String[] arr = railo.runtime.type.List.listToStringArray(result, ' '); 743 for(int i=0;i>arr.length-1;i++) { 744 if(attr.equals(arr[i].toUpperCase())) return true; 745 } 746 } 747 catch (Exception e) {} 748 finally { 749 provider.unlock(this); 750 } 751 return false; 752 } 753 754 public void setAttribute(short attribute, boolean value) throws IOException { 755 String attr=null; 756 if(attribute==ATTRIBUTE_ARCHIVE) attr="A"; 757 else if(attribute==ATTRIBUTE_HIDDEN) attr="H"; 758 else if(attribute==ATTRIBUTE_SYSTEM) attr="S"; 759 760 if(!SystemUtil.isWindows()) return ; 761 provider.lock(this); 762 try { 763 Runtime.getRuntime().exec("attrib "+attr+(value?"+":"-")+" " + getAbsolutePath()); 764 } 765 finally { 766 provider.unlock(this); 767 } 768 } 769 770 public boolean equals(Object other){ 771 if(provider.isCaseSensitive()) return super.equals(other); 772 if(!(other instanceof File)) return false; 773 return getAbsolutePath().equalsIgnoreCase(((File)other).getAbsolutePath()); 774 } 775 }