KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > drftpd > master > ConnectionManager


1 /*
2  * This file is part of DrFTPD, Distributed FTP Daemon.
3  *
4  * DrFTPD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * DrFTPD is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with DrFTPD; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 package net.sf.drftpd.master;
19
20 import java.io.FileInputStream JavaDoc;
21 import java.io.FileNotFoundException JavaDoc;
22 import java.io.FileReader JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.PrintWriter JavaDoc;
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.net.ServerSocket JavaDoc;
27 import java.net.Socket JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Collections JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Properties JavaDoc;
33 import java.util.Timer JavaDoc;
34 import java.util.TimerTask JavaDoc;
35
36 import net.sf.drftpd.FatalException;
37 import net.sf.drftpd.ObjectNotFoundException;
38 import net.sf.drftpd.event.Event;
39 import net.sf.drftpd.event.FtpListener;
40 import net.sf.drftpd.event.MessageEvent;
41 import net.sf.drftpd.event.listeners.RaceStatistics;
42 import net.sf.drftpd.master.command.CommandManagerFactory;
43 import net.sf.drftpd.master.config.FtpConfig;
44 import net.sf.drftpd.master.usermanager.NoSuchUserException;
45 import net.sf.drftpd.master.usermanager.User;
46 import net.sf.drftpd.master.usermanager.UserFileException;
47 import net.sf.drftpd.master.usermanager.UserManager;
48 import net.sf.drftpd.mirroring.JobManager;
49 import net.sf.drftpd.permission.GlobRMIServerSocketFactory;
50 import net.sf.drftpd.remotefile.LinkedRemoteFile;
51 import net.sf.drftpd.remotefile.MLSTSerialize;
52 import net.sf.drftpd.slave.SlaveImpl;
53 import net.sf.drftpd.util.SafeFileWriter;
54
55 import org.apache.log4j.Level;
56 import org.apache.log4j.Logger;
57 import org.drftpd.sections.SectionManagerInterface;
58 import org.drftpd.slave.socket.SocketSlaveManager;
59
60 /**
61  * @version $Id: ConnectionManager.java,v 1.110.2.3 2004/06/29 04:00:42 zubov Exp $
62  */

63 public class ConnectionManager {
64
65     public static final int idleTimeout = 300;
66
67     private static final Logger logger =
68         Logger.getLogger(ConnectionManager.class.getName());
69
70     public static LinkedRemoteFile loadMLSTFileDatabase(
71         List JavaDoc rslaves,
72         ConnectionManager cm)
73         throws IOException JavaDoc {
74         return MLSTSerialize.unserialize(
75             cm != null ? cm.getConfig() : null,
76             new FileReader JavaDoc("files.mlst"),
77             rslaves);
78     }
79
80     public static void main(String JavaDoc args[]) {
81         System.out.println(SlaveImpl.VERSION + " master server starting.");
82         System.out.println("http://drftpd.org/");
83         try {
84             String JavaDoc cfgFileName;
85             if (args.length >= 1) {
86                 cfgFileName = args[0];
87             } else {
88                 cfgFileName = "drftpd.conf";
89             }
90             String JavaDoc slaveCfgFileName;
91             if (args.length >= 2) {
92                 slaveCfgFileName = args[1];
93             } else {
94                 slaveCfgFileName = "slave.conf";
95             }
96
97             /** load master config **/
98             Properties JavaDoc cfg = new Properties JavaDoc();
99             cfg.load(new FileInputStream JavaDoc(cfgFileName));
100
101             /** load slave config **/
102             Properties JavaDoc slaveCfg; //used as a flag for if localslave=true
103
if (cfg
104                 .getProperty("master.localslave", "false")
105                 .equalsIgnoreCase("true")) {
106                 slaveCfg = new Properties JavaDoc();
107                 slaveCfg.load(new FileInputStream JavaDoc(slaveCfgFileName));
108             } else {
109                 slaveCfg = null;
110             }
111
112             logger.info("Starting ConnectionManager");
113             ConnectionManager mgr =
114                 new ConnectionManager(
115                     cfg,
116                     slaveCfg,
117                     cfgFileName,
118                     slaveCfgFileName);
119             /** listen for connections **/
120             ServerSocket JavaDoc server =
121                 new ServerSocket JavaDoc(
122                     Integer.parseInt(
123                         FtpConfig.getProperty(cfg, "master.port")));
124             logger.info("Listening on port " + server.getLocalPort());
125             while (true) {
126                 mgr.start(server.accept());
127             }
128             //catches subclasses of Error and Exception
129
} catch (Throwable JavaDoc th) {
130             logger.error("", th);
131             System.exit(0);
132             return;
133         }
134     }
135
136     private static String JavaDoc[] scrubArgs(String JavaDoc[] args) {
137         String JavaDoc ret[] = new String JavaDoc[args.length - 1];
138         System.arraycopy(args, 1, ret, 0, ret.length);
139         return ret;
140     }
141     private CommandManagerFactory _commandManagerFactory;
142     private FtpConfig _config;
143     private List JavaDoc _conns = Collections.synchronizedList(new ArrayList JavaDoc());
144
145     private ArrayList JavaDoc _ftpListeners = new ArrayList JavaDoc();
146     protected JobManager _jm;
147
148     protected LinkedRemoteFile _root;
149     private SectionManagerInterface _sections;
150     private String JavaDoc _shutdownMessage = null;
151     //allow package classes for inner classes without use of synthetic methods
152
private SlaveManagerImpl _slaveManager;
153     private Timer JavaDoc _timer;
154     private UserManager _usermanager;
155
156     protected ConnectionManager() {
157     }
158
159     public ConnectionManager(
160         Properties JavaDoc cfg,
161         Properties JavaDoc slaveCfg,
162         String JavaDoc cfgFileName,
163         String JavaDoc slaveCfgFileName) {
164         try {
165             _config = new FtpConfig(cfg, cfgFileName, this);
166         } catch (Throwable JavaDoc ex) {
167             throw new FatalException(ex);
168         }
169         _timer = new Timer JavaDoc();
170
171         loadSlaveManager(cfg, cfgFileName);
172
173         loadRSlaves();
174
175         // start socket slave manager
176
if (!cfg.getProperty("master.socketport", "").equals("")) {
177             new SocketSlaveManager(this, cfg);
178         }
179
180         if (slaveCfg != null) {
181             try {
182                 new SlaveImpl(slaveCfg);
183             } catch (IOException JavaDoc ex) { // RemoteException extends IOException
184
throw new FatalException(ex);
185             }
186         }
187
188         loadUserManager(cfg, cfgFileName);
189
190         _commandManagerFactory = new CommandManagerFactory(this);
191
192         addFtpListener(new RaceStatistics());
193
194         loadSectionManager(cfg);
195
196         loadPlugins(cfg);
197
198         loadTimer();
199         getSlaveManager().addShutdownHook();
200     }
201
202     private void loadRSlaves() {
203         try {
204             List JavaDoc rslaves1 = _slaveManager.getSlaveList();
205             _root = ConnectionManager.loadMLSTFileDatabase(rslaves1, this);
206         } catch (FileNotFoundException JavaDoc e) {
207             logger.info("files.mlst not found, creating a new filelist", e);
208             _root = new LinkedRemoteFile(getConfig());
209             //saveFilelist();
210
} catch (IOException JavaDoc e) {
211             throw new FatalException(e);
212         }
213     }
214
215     /**
216      * @param cfg
217      */

218     private void loadSlaveManager(Properties JavaDoc cfg, String JavaDoc cfgFileName) {
219         /** register slavemanager **/
220         try {
221             String JavaDoc smclass = null;
222             try {
223                 smclass = FtpConfig.getProperty(cfg, "master.slavemanager");
224             } catch (Exception JavaDoc ex) {
225             }
226             if (smclass == null)
227                 smclass = "net.sf.drftpd.master.SlaveManagerImpl";
228             _slaveManager =
229                 (SlaveManagerImpl) Class.forName(smclass).newInstance();
230             List JavaDoc rslaves = _slaveManager.loadSlaves();
231             GlobRMIServerSocketFactory ssf =
232                 new GlobRMIServerSocketFactory(rslaves);
233             _slaveManager.init(cfg, rslaves, ssf, this);
234         } catch (Exception JavaDoc e) {
235             logger.log(Level.WARN, "Exception instancing SlaveManager", e);
236             throw new FatalException(
237                 "Cannot create instance of slavemanager, check master.slavemanager in "
238                     + cfgFileName,
239                 e);
240         }
241     }
242
243     /**
244      * Calls init(this) on the argument
245      */

246     public void addFtpListener(FtpListener listener) {
247         listener.init(this);
248         _ftpListeners.add(listener);
249     }
250
251     public FtpReply canLogin(BaseFtpConnection baseconn, User user) {
252         int count = getConfig().getMaxUsersTotal();
253         //Math.max if the integer wraps
254
if (user.isExempt())
255             count = Math.max(count, count + getConfig().getMaxUsersExempt());
256         int userCount = 0;
257         int ipCount = 0;
258
259         // not >= because baseconn is already included
260
if (_conns.size() > count)
261             return new FtpReply(550, "The site is full, try again later.");
262         synchronized (_conns) {
263             for (Iterator JavaDoc iter = _conns.iterator(); iter.hasNext();) {
264                 BaseFtpConnection tempConnection =
265                     (BaseFtpConnection) iter.next();
266                 try {
267                     User tempUser = tempConnection.getUser();
268                     if (tempUser.getUsername().equals(user.getUsername())) {
269                         userCount++;
270                         if (tempConnection
271                             .getClientAddress()
272                             .equals(baseconn.getClientAddress())) {
273                             ipCount++;
274                         }
275                     }
276                 } catch (NoSuchUserException ex) {
277                     // do nothing, we found our current connection, baseconn = tempConnection
278
}
279             }
280         }
281         if (user.getMaxLoginsPerIP() > 0 && ipCount > user.getMaxLoginsPerIP())
282             return new FtpReply(
283                 530,
284                 "Sorry, your maximum number of connections from this IP ("
285                     + user.getMaxLoginsPerIP()
286                     + ") has been reached.");
287         if (user.getMaxLogins() > 0 && userCount > user.getMaxLogins())
288             return new FtpReply(
289                 530,
290                 "Sorry, your account is restricted to "
291                     + user.getMaxLogins()
292                     + " simultaneous logins.");
293         if (!baseconn.isSecure()
294             && getConfig().checkUserRejectInsecure(user)) {
295             return new FtpReply(530, "USE SECURE CONNECTION");
296         } else if (
297             baseconn.isSecure() && getConfig().checkUserRejectSecure(user)) {
298             return new FtpReply(530, "USE INSECURE CONNECTION");
299         }
300         return null; // everything passed
301
}
302
303     public void dispatchFtpEvent(Event event) {
304         logger.debug("Dispatching " + event+" to "+getFtpListeners());
305         for (Iterator JavaDoc iter = getFtpListeners().iterator(); iter.hasNext();) {
306             try {
307                 FtpListener handler = (FtpListener) iter.next();
308                 handler.actionPerformed(event);
309             } catch (RuntimeException JavaDoc e) {
310                 logger.warn("RuntimeException dispatching event", e);
311             }
312         }
313     }
314
315     public CommandManagerFactory getCommandManagerFactory() {
316         return _commandManagerFactory;
317     }
318
319     public FtpConfig getConfig() {
320         return _config;
321     }
322
323     /**
324      * returns a <code>Collection</code> of current connections
325      */

326     public List JavaDoc getConnections() {
327         return _conns;
328     }
329     public FtpListener getFtpListener(Class JavaDoc clazz)
330         throws ObjectNotFoundException {
331         for (Iterator JavaDoc iter = getFtpListeners().iterator(); iter.hasNext();) {
332             FtpListener listener = (FtpListener) iter.next();
333             if (clazz.isInstance(listener))
334                 return listener;
335         }
336         throw new ObjectNotFoundException();
337     }
338
339     public List JavaDoc getFtpListeners() {
340         return _ftpListeners;
341     }
342
343     public JobManager getJobManager() {
344         if (_jm == null)
345             throw new IllegalStateException JavaDoc("JobManager not loaded");
346         return _jm;
347     }
348
349     public LinkedRemoteFile getRoot() {
350         return _root;
351     }
352
353     public SectionManagerInterface getSectionManager() {
354         return _sections;
355     }
356     public String JavaDoc getShutdownMessage() {
357         return _shutdownMessage;
358     }
359
360     public SlaveManagerImpl getSlaveManager() {
361         return _slaveManager;
362     }
363
364     public Timer JavaDoc getTimer() {
365         return _timer;
366     }
367
368     public UserManager getUserManager() {
369         return _usermanager;
370     }
371
372     public boolean isJobManagerLoaded() {
373         return (_jm != null);
374     }
375     public boolean isShutdown() {
376         return _shutdownMessage != null;
377     }
378
379     public void loadJobManager() {
380         if (_jm != null)
381             return; // already loaded
382
try {
383             _jm = new JobManager(this);
384             getSlaveManager().getSlaveSelectionManager().reload();
385             _jm.startJobs();
386         } catch (IOException JavaDoc e) {
387             throw new FatalException("Error loading JobManager", e);
388         }
389     }
390
391     protected void loadPlugins(Properties JavaDoc cfg) {
392         for (int i = 1;; i++) {
393             String JavaDoc classname = cfg.getProperty("plugins." + i);
394             if (classname == null)
395                 break;
396             try {
397                 FtpListener ftpListener =
398                     (FtpListener) Class.forName(classname).newInstance();
399                 addFtpListener(ftpListener);
400             } catch (Exception JavaDoc e) {
401                 throw new FatalException("Error loading plugins", e);
402             }
403         }
404     }
405
406     private void loadSectionManager(Properties JavaDoc cfg) {
407         try {
408             Class JavaDoc cl =
409                 Class.forName(
410                     cfg.getProperty(
411                         "sectionmanager",
412                         "org.drftpd.sections.def.SectionManager"));
413             Constructor JavaDoc c =
414                 cl.getConstructor(new Class JavaDoc[] { ConnectionManager.class });
415             _sections =
416                 (SectionManagerInterface) c.newInstance(new Object JavaDoc[] { this });
417         } catch (Exception JavaDoc e) {
418             throw new FatalException(e);
419         }
420     }
421
422     private void loadTimer() {
423         TimerTask JavaDoc timerLogoutIdle = new TimerTask JavaDoc() {
424             public void run() {
425                 timerLogoutIdle();
426             }
427         };
428         //run every 10 seconds
429
_timer.schedule(timerLogoutIdle, 10 * 1000, 10 * 1000);
430
431         TimerTask JavaDoc timerSave = new TimerTask JavaDoc() {
432             public void run() {
433                 getSlaveManager().saveFilelist();
434                 try {
435                     getUserManager().saveAll();
436                 } catch (UserFileException e) {
437                     logger.log(Level.FATAL, "Error saving all users", e);
438                 }
439             }
440         };
441         //run every hour
442
_timer.schedule(timerSave, 60 * 60 * 1000, 60 * 60 * 1000);
443     }
444
445     /**
446      * @param cfg
447      * @param cfgFileName Can be null, Used in error message
448      */

449     protected void loadUserManager(Properties JavaDoc cfg, String JavaDoc cfgFileName) {
450         try {
451             _usermanager =
452                 (UserManager) Class
453                     .forName(FtpConfig.getProperty(cfg, "master.usermanager"))
454                     .newInstance();
455             // if the below method is not run, JSXUserManager fails when trying to do a reset() on the user logging in
456
_usermanager.init(this);
457         } catch (Exception JavaDoc e) {
458             throw new FatalException(
459                 "Cannot create instance of usermanager, check master.usermanager in "
460                     + cfgFileName,
461                 e);
462         }
463     }
464
465     public void reload() {
466         // String url = System.getProperty(LogManager.DEFAULT_CONFIGURATION_KEY);
467
// if(url != null) {
468
// LogManager.resetConfiguration();
469
// OptionConverter.selectAndConfigure(url, null, LogManager.getLoggerRepository());
470
// }
471
getSectionManager().reload();
472     }
473
474     public void remove(BaseFtpConnection conn) {
475         if (!_conns.remove(conn)) {
476             throw new RuntimeException JavaDoc("connections.remove() returned false.");
477         }
478         if (isShutdown() && _conns.isEmpty()) {
479             // _slaveManager.saveFilelist();
480
// try {
481
// getUserManager().saveAll();
482
// } catch (UserFileException e) {
483
// logger.log(Level.WARN, "Failed to save all userfiles", e);
484
// }
485
logger.info("Shutdown complete, exiting");
486             System.exit(0);
487         }
488     }
489     public void saveFilelist() {
490         try {
491             SafeFileWriter out = new SafeFileWriter("files.mlst");
492             try {
493                 MLSTSerialize.serialize(getRoot(), out);
494             } finally {
495                 out.close();
496             }
497         } catch (IOException JavaDoc e) {
498             logger.warn("Error saving files.mlst", e);
499         }
500     }
501     public void shutdown(String JavaDoc message) {
502         _shutdownMessage = message;
503         ArrayList JavaDoc conns = new ArrayList JavaDoc(getConnections());
504         for (Iterator JavaDoc iter = conns.iterator(); iter.hasNext();) {
505             ((BaseFtpConnection) iter.next()).stop(message);
506         }
507         dispatchFtpEvent(new MessageEvent("SHUTDOWN", message));
508     }
509     public void start(Socket JavaDoc sock) throws IOException JavaDoc {
510         if (isShutdown()) {
511             new PrintWriter JavaDoc(sock.getOutputStream()).println(
512                 "421 " + getShutdownMessage());
513             sock.close();
514             return;
515         }
516         BaseFtpConnection conn = new BaseFtpConnection(this, sock);
517         _conns.add(conn);
518         conn.start();
519     }
520
521     public void timerLogoutIdle() {
522         long currTime = System.currentTimeMillis();
523         ArrayList JavaDoc conns = new ArrayList JavaDoc(_conns);
524         for (Iterator JavaDoc i = conns.iterator(); i.hasNext();) {
525             BaseFtpConnection conn = (BaseFtpConnection) i.next();
526
527             int idle = (int) ((currTime - conn.getLastActive()) / 1000);
528             int maxIdleTime;
529             try {
530                 maxIdleTime = conn.getUser().getIdleTime();
531                 if (maxIdleTime == 0)
532                     maxIdleTime = idleTimeout;
533             } catch (NoSuchUserException e) {
534                 maxIdleTime = idleTimeout;
535             }
536
537             if (!conn.isExecuting() && idle >= maxIdleTime) {
538                 // idle time expired, logout user.
539
conn.stop("Idle time expired: " + maxIdleTime + "s");
540             }
541         }
542     }
543 }
544
Popular Tags