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.ftp;
020
021import java.io.IOException;
022import java.util.Map;
023
024import lucee.commons.collection.MapFactory;
025import lucee.commons.lang.SerializableObject;
026import lucee.commons.lang.StringUtil;
027
028import org.apache.commons.net.ftp.FTPClient;
029import org.apache.commons.net.ftp.FTPFile;
030
031public final class FTPResourceClient extends FTPClient {
032
033        private String workingDirectory=null;
034        
035        
036        private final FTPConnectionData ftpConnectionData;
037        private long lastAccess;
038        private final Object token=new SerializableObject();
039        private final Map<String,FTPFileWrap> files=MapFactory.<String,FTPFileWrap>getConcurrentMap();
040        private final int cacheTimeout;
041
042        public FTPResourceClient(FTPConnectionData ftpConnectionData,int cacheTimeout) {
043                this.ftpConnectionData=ftpConnectionData;
044                this.cacheTimeout=cacheTimeout;
045        }
046
047        /**
048         * @return the ftpConnectionData
049         */
050        public FTPConnectionData getFtpConnectionData() {
051                return ftpConnectionData;
052        }
053
054        public void touch() {
055                this.lastAccess=System.currentTimeMillis();
056        }
057
058        /**
059         * @return the lastAccess
060         */
061        public long getLastAccess() {
062                return lastAccess;
063        }
064
065        public Object getToken() {
066                return token;
067        }
068        
069        @Override
070        public boolean changeWorkingDirectory(String pathname) throws IOException {
071                if(StringUtil.endsWith(pathname,'/') && pathname.length()!=1)pathname=pathname.substring(0,pathname.length()-1);
072                
073                if(pathname.equals(workingDirectory)) return true;
074                workingDirectory=pathname;
075                return super.changeWorkingDirectory(pathname);
076        }
077
078        public String id() {
079                return ftpConnectionData.key();
080        }
081
082        @Override
083        public boolean equals(Object obj) {
084                
085                return ((FTPResourceClient)obj).id().equals(id());
086        }
087
088        public FTPFile getFTPFile(FTPResource res) throws IOException {
089                String path=res.getInnerPath();
090                FTPFileWrap fw = files.get(path);
091                
092                if(fw==null) {
093                        return createFTPFile(res);
094                }
095                if(fw.time+cacheTimeout<System.currentTimeMillis()) {
096                        files.remove(path);
097                        return createFTPFile(res);
098                }
099                return fw.file;
100        }
101        public void registerFTPFile(FTPResource res,FTPFile file) {
102                files.put(res.getInnerPath(),new FTPFileWrap(file));    
103        }
104
105        public void unregisterFTPFile(FTPResource res) {
106                files.remove(res.getInnerPath());
107        }
108        
109        
110        private FTPFile createFTPFile(FTPResource res) throws IOException {
111                FTPFile[] children=null;
112                boolean isRoot=res.isRoot();
113                String path=isRoot?res.getInnerPath():res.getInnerParent();
114                
115                synchronized(getToken()){ 
116                        changeWorkingDirectory(path);
117                        children = listFiles();
118                }
119                
120                if(children.length>0) {
121                        for(int i=0;i<children.length;i++) {
122                                if(isRoot){
123                                        if(children[i].getName().equals(".")) {
124                                                registerFTPFile(res, children[i]);
125                                                return children[i];
126                                        }
127                                }
128                                else{
129                                        if(children[i].getName().equals(res.getName())) {
130                                                registerFTPFile(res, children[i]);
131                                                return children[i];
132                                        }
133                                }
134                        }
135                }
136                return null;
137        }
138        
139        @Override
140        public boolean deleteFile(String pathname) throws IOException {
141                files.remove(pathname);
142                return super.deleteFile(pathname);
143        }
144
145        class FTPFileWrap {
146
147                private FTPFile file;
148                private long time;
149
150                public FTPFileWrap(FTPFile file) {
151                        this.file=file;
152                        this.time=System.currentTimeMillis();
153                }
154                
155        }
156}