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    }