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