001    package railo.commons.io.res.type.ftp;
002    
003    
004    
005    import java.io.IOException;
006    import java.net.SocketException;
007    import java.util.HashMap;
008    import java.util.Map;
009    
010    import railo.commons.io.res.Resource;
011    import railo.commons.io.res.ResourceProvider;
012    import railo.commons.io.res.Resources;
013    import railo.commons.io.res.util.ResourceLockImpl;
014    import railo.commons.io.res.util.ResourceUtil;
015    import railo.commons.lang.SizeOf;
016    import railo.commons.lang.StringUtil;
017    import railo.runtime.net.proxy.Proxy;
018    import railo.runtime.op.Caster;
019    import railo.runtime.type.Sizeable;
020    
021    // TODO check connection timeout
022    public final class FTPResourceProvider implements ResourceProvider,Sizeable {
023            private String scheme="ftp";
024            private final Map clients=new HashMap();
025            private int clientTimeout=60000;
026            private int socketTimeout=-1;
027            private int lockTimeout=20000;
028            private int cache=20000;
029    
030            private FTPResourceClientCloser closer=null;
031            private final ResourceLockImpl lock=new ResourceLockImpl(lockTimeout,true);
032            private Map arguments;
033    
034            public ResourceProvider init(String scheme, Map arguments) {
035                    setScheme(scheme);
036                    
037                    if(arguments!=null) {
038                            this.arguments=arguments;
039                            // client-timeout
040                            String strTimeout=(String) arguments.get("client-timeout");
041                            if(strTimeout!=null) {
042                                    clientTimeout=Caster.toIntValue(strTimeout,clientTimeout);
043                            }
044                            // socket-timeout
045                            strTimeout=(String) arguments.get("socket-timeout");
046                            if(strTimeout!=null) {
047                                    socketTimeout=Caster.toIntValue(strTimeout,socketTimeout);
048                            }
049                            // lock-timeout
050                            strTimeout=(String) arguments.get("lock-timeout");
051                            if(strTimeout!=null) {
052                                    lockTimeout=Caster.toIntValue(strTimeout,lockTimeout);
053                            }
054                            // cache
055                            String strCache=(String) arguments.get("cache");
056                            if(strCache!=null) {
057                                    cache=Caster.toIntValue(strCache,cache);
058                            }
059                    }
060                    lock.setLockTimeout(lockTimeout);
061                    
062                    return this;
063            }
064            
065            @Override
066            public Resource getResource(String path) {
067                    path=ResourceUtil.removeScheme(scheme,path);
068                    FTPConnectionData data=new FTPConnectionData();
069                    path=data.load(path);
070                    
071                    return  new FTPResource(this,data,path);
072            }
073                    
074                    
075            
076    
077            FTPResourceClient getClient(FTPConnectionData data) throws IOException {
078                    
079                    FTPResourceClient client=(FTPResourceClient) clients.remove(data.key());
080                    if(client==null) {
081                            client = new FTPResourceClient(data,cache);
082                            if(socketTimeout>0)client.setSoTimeout(socketTimeout);
083                    }
084                    
085                    if(!client.isConnected()) { 
086                            if(data.hasProxyData()) {
087                                    try {
088                                    Proxy.start(
089                                            data.getProxyserver(), 
090                                            data.getProxyport(), 
091                                            data.getProxyuser(), 
092                                            data.getProxypassword()
093                                );
094                                    connect(client,data);
095                            }
096                            finally {
097                                    Proxy.end();
098                            }
099                            }
100                            else {
101                                    connect(client,data);
102                            }
103                            
104                            int replyCode = client.getReplyCode();
105                            if(replyCode>=400)
106                                    throw new FTPException(replyCode);
107                    }
108                    startCloser();
109                    return client;
110            }
111                    
112            private synchronized void startCloser() {
113                    if(closer==null || !closer.isAlive()) {
114                            closer=new FTPResourceClientCloser(this);
115                            closer.start();
116                    }
117                    
118            }
119            private void connect(FTPResourceClient client, FTPConnectionData data) throws SocketException, IOException {
120                    if(data.port>0)client.connect(data.host,data.port);
121                    else client.connect(data.host);
122                    if(!StringUtil.isEmpty(data.username))client.login(data.username,data.password);
123            }
124    
125            public void returnClient(FTPResourceClient client) {
126                    if(client==null)return;
127                    client.touch();
128                    clients.put(client.getFtpConnectionData().key(), client);
129            }
130    
131            @Override
132            public String getScheme() {
133                    return scheme;
134            }
135    
136            public void setScheme(String scheme) {
137                    if(!StringUtil.isEmpty(scheme))this.scheme=scheme;
138            }
139    
140            @Override
141            public void setResources(Resources resources) {
142                    //this.resources=resources;
143            }
144            
145            @Override
146            public void lock(Resource res) throws IOException {
147                    lock.lock(res);
148            }
149    
150            @Override
151            public void unlock(Resource res) {
152                    lock.unlock(res);
153            }
154    
155            @Override
156            public void read(Resource res) throws IOException {
157                    lock.read(res);
158            }
159            
160            public void clean() {
161                    Object[] keys = clients.keySet().toArray();
162                    FTPResourceClient client;
163                    for(int i=0;i<keys.length;i++) {
164                            client=(FTPResourceClient) clients.get(keys[i]);
165                            if(client.getLastAccess()+clientTimeout<System.currentTimeMillis()) {
166                                    //railo.print.ln("disconnect:"+client.getFtpConnectionData().key());
167                                    if(client.isConnected()) {
168                                            try {
169                                                    client.disconnect();
170                                            } 
171                                            catch (IOException e) {}
172                                    }
173                                    clients.remove(client.getFtpConnectionData().key());
174                            }
175                    }
176            }
177            
178            class FTPResourceClientCloser extends Thread {
179    
180                    private FTPResourceProvider provider;
181    
182                    public FTPResourceClientCloser(FTPResourceProvider provider) {
183                            this.provider=provider;
184                    }
185                    
186                    public void run() {
187                            //railo.print.ln("closer start");
188                            do {
189                                    sleepEL();
190                                    provider.clean();
191                            }
192                            while(!clients.isEmpty());
193                            //railo.print.ln("closer stop");
194                    }
195    
196                    private void sleepEL() {
197                            try {
198                                    sleep(provider.clientTimeout);
199                            } catch (InterruptedException e) {}             
200                    }
201            }
202            
203            /**
204             * @return the cache
205             */
206            public int getCache() {
207                    return cache;
208            }
209    
210            @Override
211            public boolean isAttributesSupported() {
212                    return false;
213            }
214    
215            public boolean isCaseSensitive() {
216                    return true;
217            }
218    
219            @Override
220            public boolean isModeSupported() {
221                    return true;
222            }
223    
224            @Override
225            public long sizeOf() {
226                    return SizeOf.size(lock)+SizeOf.size(clients);
227            }
228    
229            @Override
230            public Map getArguments() {
231                    return arguments;
232            }
233    
234    }
235