001: /*
002: * Lucane - a collaborative platform
003: * Copyright (C) 2002 Gautier Ringeisen <gautier_ringeisen@hotmail.com>
004: * Copyright (C) 2004 Vincent Fiack <vfiack@mail15.com>
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package org.lucane.server;
021:
022: import org.lucane.common.concepts.*;
023: import org.lucane.common.crypto.KeyTool;
024: import org.lucane.server.acl.AccessController;
025: import org.lucane.server.auth.Authenticator;
026: import org.lucane.server.database.*;
027: import org.lucane.server.store.*;
028: import org.lucane.common.*;
029: import org.lucane.common.net.ClientSocketFactory;
030: import org.lucane.common.net.ObjectConnection;
031: import org.lucane.common.net.ServerSocketFactory;
032:
033: import java.io.*;
034: import java.net.*;
035: import java.util.*;
036:
037: /**
038: * The server's class
039: */
040: public class Server {
041:
042: private String workingDirectory;
043:
044: private static final String CONFIG_FILE = "etc/server-config.xml";
045: private static final String LOG_FILE = "logs/lucane.log";
046: public static final String APPLICATIONS_DIRECTORY = "applications/";
047: public static final String VERSION = "0.7.5";
048:
049: //-- instance management
050: private static Server instance = null;
051:
052: public static Server getInstance() {
053: return instance;
054: }
055:
056: //-- attributes
057: private ServerConfig config;
058:
059: private ServerSocket plainSocket;
060: private ServerSocket sslSocket;
061: private Store store;
062: private Authenticator authenticator;
063: private AccessController accessController;
064: private DatabaseAbstractionLayer dbLayer;
065: private String privateKey;
066:
067: /**
068: * Creates a new Server object.
069: */
070: private Server(ServerConfig config, String workingDirectory) {
071: Server.instance = this ;
072: this .config = config;
073: this .workingDirectory = workingDirectory;
074: this .dbLayer = null;
075: this .privateKey = "nokey";
076:
077: try {
078: dbLayer = DatabaseAbstractionLayer.createLayer(config);
079: Logging.getLogger().finer("dbLayer : " + dbLayer);
080:
081: this .store = new Store(config);
082: } catch (Exception ex) {
083: Logging.getLogger().severe(
084: "#Err > Unable to connect to the database : "
085: + ex.getMessage());
086: ex.printStackTrace();
087: System.exit(1);
088: }
089:
090: try {
091: this .authenticator = Authenticator.newInstance(config);
092: } catch (Exception e) {
093: Logging.getLogger().severe(
094: "Unable to get Authenticator instance.");
095: e.printStackTrace();
096: System.exit(1);
097: }
098:
099: try {
100: this .accessController = AccessController
101: .newInstance(config);
102: } catch (Exception e) {
103: Logging.getLogger().severe(
104: "Unable to get AccessController instance.");
105: e.printStackTrace();
106: System.exit(1);
107: }
108: }
109:
110: /**
111: * Create server's ConnectInfo
112: */
113: private void initConnectInfo() {
114: int port = config.getPort();
115: String publicKey = "nokey";
116:
117: if (config.isSslEnabled()) {
118: port = config.getSslPort();
119:
120: try {
121: this .privateKey = KeyTool.createPrivateStore("server",
122: config.getSslPassword());
123: publicKey = KeyTool.createPublicStore(privateKey,
124: "server");
125: } catch (Exception e) {
126: Logging.getLogger().severe(
127: "Unable to generate keypair : " + e);
128: System.exit(1);
129: }
130: }
131:
132: ConnectInfo myConnectInfo = new ConnectInfo("server",
133: "not-used", port, publicKey, "Server");
134: ConnectInfoManager.getInstance().setServerInfo(myConnectInfo);
135: ConnectInfoManager.getInstance().addConnectInfo(myConnectInfo);
136: }
137:
138: /**
139: * Accepts connections
140: */
141: public void acceptConnections() {
142: if (config.isSslEnabled())
143: this .acceptSslConnections();
144:
145: this .acceptPlainConnections();
146: }
147:
148: private void acceptPlainConnections() {
149: try {
150: this .plainSocket = ServerSocketFactory
151: .getServerSocket(config.getPort());
152: } catch (Exception e) {
153: Logging.getLogger().severe(
154: "#Err > Unable to listen on the port "
155: + config.getPort() + ".");
156: e.printStackTrace();
157: System.exit(1);
158: }
159:
160: Runnable plain = new Runnable() {
161: public void run() {
162: while (!plainSocket.isClosed()) {
163: try {
164: MessageHandler handler = new MessageHandler(
165: plainSocket.accept());
166: handler.start();
167: } catch (IOException ex) {
168: if (!plainSocket.isClosed())
169: Logging
170: .getLogger()
171: .warning(
172: "#Err > Unable to accept connections.");
173: }
174: }
175: }
176: };
177:
178: new Thread(plain).start();
179: Logging.getLogger().info(
180: "Accepting plain connections on port "
181: + config.getPort());
182: }
183:
184: private void acceptSslConnections() {
185: try {
186: this .sslSocket = ServerSocketFactory.getServerSocket(config
187: .getSslPort(), this .privateKey, "server", config
188: .getSslPassword());
189: } catch (Exception e) {
190: Logging.getLogger().severe(
191: "#Err > Unable to listen on the port "
192: + config.getSslPort() + ".");
193: e.printStackTrace();
194: System.exit(1);
195: }
196:
197: Runnable ssl = new Runnable() {
198: public void run() {
199: while (!sslSocket.isClosed()) {
200: try {
201: MessageHandler handler = new MessageHandler(
202: sslSocket.accept());
203: handler.start();
204: } catch (IOException ex) {
205: if (!sslSocket.isClosed())
206: Logging
207: .getLogger()
208: .warning(
209: "#Err > Unable to accept connections.");
210: }
211: }
212: }
213: };
214:
215: new Thread(ssl).start();
216: Logging.getLogger().info(
217: "Accepting SSL connections on port "
218: + config.getSslPort());
219: }
220:
221: /**
222: * Stop the server
223: */
224: public void shutdown() {
225: this .authenticator.disableLogin();
226: ConnectInfoManager.getInstance().kickAllUsers();
227: Logging.getLogger().info("Users disconnected.");
228: ServiceManager.getInstance().shutdownAllServices();
229: Logging.getLogger().info("Services shutdowned.");
230:
231: try {
232: this .plainSocket.close();
233: if (config.isSslEnabled())
234: this .sslSocket.close();
235:
236: Logging.getLogger().info("Socket closed.");
237: } catch (IOException e) {
238: Logging.getLogger()
239: .warning("Unable to close socket : " + e);
240: }
241:
242: System.exit(0);
243: }
244:
245: /**
246: * Send the ConnectInfo associated with this name
247: *
248: * @param name the user wanted
249: */
250: public void sendConnectInfo(String name, ObjectConnection oc) {
251: ConnectInfo ci = ConnectInfoManager.getInstance()
252: .getConnectInfo(name);
253: if (ci != null) {
254: try {
255: oc.write(ci);
256: } catch (Exception e) {
257: }
258: } else {
259: try {
260: oc.write("unknown");
261: } catch (Exception e) {
262: }
263: }
264: }
265:
266: /**
267: * Send the connected users list
268: */
269: public void sendUserList(ObjectConnection oc) {
270: //get into vector
271: Vector v = new Vector();
272: Iterator i = ConnectInfoManager.getInstance()
273: .getClientConnectInfos();
274: while (i.hasNext())
275: v.addElement(i.next());
276:
277: //send
278: try {
279: oc.write(v);
280: } catch (Exception e) {
281: }
282: }
283:
284: public void sendServerConnectInfo(ObjectConnection oc) {
285: try {
286: oc.write(ConnectInfoManager.getInstance().getServerInfo());
287: } catch (Exception e) {
288: e.printStackTrace();
289: }
290: }
291:
292: /**
293: * Send the plugin list to a client.
294: * The list depends of the client's groups
295: *
296: * @param source the user that asked this command
297: */
298: public void sendPluginList(ObjectConnection oc, String source) {
299: Vector plist = new Vector();
300: Iterator plugins;
301: String line = "";
302:
303: try {
304: UserConcept user = store.getUserStore().getUser(source);
305: plugins = store.getPluginStore().getAuthorizedPlugins(user);
306:
307: while (plugins.hasNext()) {
308: PluginConcept plugin = (PluginConcept) plugins.next();
309: line = plugin.getName();
310: line += " " + plugin.getVersion();
311: plist.add(line);
312: }
313: oc.write(plist);
314: } catch (Exception e) {
315: Logging.getLogger().warning(
316: "Unable to send the plugin list.");
317: e.printStackTrace();
318: }
319: }
320:
321: /**
322: * Send a plugin JAR file to the client
323: *
324: * @param data the plugin name
325: */
326: public void sendPluginFile(ObjectConnection oc, String data) {
327: DataInputStream dis = null;
328: try {
329: dis = new DataInputStream(new FileInputStream(
330: getWorkingDirectory() + APPLICATIONS_DIRECTORY
331: + data + ".jar"));
332: byte[] buf = new byte[dis.available()];
333: dis.readFully(buf);
334: oc.write(buf);
335: } catch (Exception e) {
336: Logging.getLogger().warning(
337: "Unable to send the file: " + data + ".jar");
338: } finally {
339: if (dis != null) {
340: try {
341: dis.close();
342: } catch (IOException ioe) {
343: }
344: }
345:
346: }
347: }
348:
349: /**
350: * Send the user's startup plugin
351: *
352: * @param source the user
353: */
354: public void sendStartupPlugin(ObjectConnection oc, String source) {
355: try {
356: UserConcept user = store.getUserStore().getUser(source);
357: oc.write(user.getStartupPlugin());
358: } catch (Exception e) {
359: }
360: }
361:
362: /**
363: * Send the user list to all users
364: */
365: public void sendUserListToEveryone() {
366: ArrayList userList = new ArrayList();
367: Iterator users = ConnectInfoManager.getInstance()
368: .getClientConnectInfos();
369: while (users.hasNext()) {
370: ConnectInfo user = (ConnectInfo) users.next();
371: userList.add(user.getName());
372: }
373:
374: Map action = new HashMap();
375: action.put("command", "USER_LIST");
376: action.put("users", userList);
377:
378: // send to everyone
379: Iterator clients = ConnectInfoManager.getInstance()
380: .getClientConnectInfos();
381: while (clients.hasNext()) {
382: ConnectInfo client = (ConnectInfo) clients.next();
383: try {
384: ObjectConnection oc = sendMessageTo(client, "Client",
385: action);
386: oc.close();
387: } catch (Exception e) {
388: Logging.getLogger().warning(
389: "unable to send user list to " + client);
390:
391: //if we can't send to the client, he's probably disconnected
392: //removeConnectInfo will call me again, that's why I break here
393: ConnectInfoManager.getInstance().removeConnectInfo(
394: client);
395: break;
396: }
397: }
398: }
399:
400: /**
401: * Send a message through the network
402: *
403: * @param dest the receiver
404: * @param app the application that need to process this message
405: * @param data the data to send
406: */
407: public ObjectConnection sendMessageTo(ConnectInfo dest, String app,
408: Object data) throws Exception {
409: Socket sock = ClientSocketFactory.getSocket(dest);
410: ObjectConnection oc = new ObjectConnection(sock);
411:
412: Message msg = new Message(ConnectInfoManager.getInstance()
413: .getServerInfo(), dest, app, data);
414: oc.write(msg);
415:
416: return oc;
417: }
418:
419: /**
420: * Get the database layer
421: *
422: * @return the Layer instance
423: */
424: public DatabaseAbstractionLayer getDBLayer() {
425: return this .dbLayer;
426: }
427:
428: /**
429: * Get the store factory
430: *
431: * @return the store factory instance
432: */
433: public Store getStore() {
434: return store;
435: }
436:
437: /**
438: * Get the authenticator
439: *
440: * @return the authenticator instance
441: */
442: public Authenticator getAuthenticator() {
443: return this .authenticator;
444: }
445:
446: /**
447: * Get the access controller
448: *
449: * @return the access controller instance
450: */
451: public AccessController getAccessController() {
452: return this .accessController;
453: }
454:
455: /**
456: * Get the working directory
457: *
458: * @return the working directory
459: */
460: public String getWorkingDirectory() {
461: return workingDirectory;
462: }
463:
464: //-- static methods
465:
466: /**
467: * Used to shutdown the server for the NT service
468: */
469: public static void shutdownServer(String[] args) {
470: Server.getInstance().shutdown();
471: }
472:
473: /**
474: * Main Method
475: *
476: * @param args command line arguments
477: */
478: public static void main(String[] args) {
479: if (args.length > 1) {
480: System.err
481: .println("USAGE :\nserver.(bat|sh) [server path]");
482: System.exit(1);
483: }
484:
485: // get the workingDirectory
486: String workingDirectory;
487: if (args.length == 1) {
488: workingDirectory = args[0];
489: } else {
490: workingDirectory = System.getProperty("user.dir");
491: }
492: workingDirectory = workingDirectory.replace('\\', '/');
493: if (workingDirectory.startsWith("\""))
494: workingDirectory = workingDirectory.substring(1,
495: workingDirectory.length() - 2);
496: if (!workingDirectory.endsWith("/"))
497: workingDirectory += "/";
498:
499: //init logging
500: try {
501: new File(workingDirectory + "/logs").mkdirs();
502: Logging.init(workingDirectory + LOG_FILE, "ALL");
503: } catch (IOException ioe) {
504: System.err.println("Unable to init logging, exiting.");
505: System.exit(1);
506: }
507:
508: ServerConfig config = null;
509:
510: try {
511: config = new ServerConfig(workingDirectory + CONFIG_FILE);
512: } catch (Exception e) {
513: Logging.getLogger().severe(
514: "Unable to read or parse the config file.");
515: e.printStackTrace();
516: System.exit(1);
517: }
518:
519: // Server creation
520: Server server = new Server(config, workingDirectory);
521: server.initConnectInfo();
522: ServiceManager.getInstance().loadAllServices();
523: ServiceManager.getInstance().startAllServices();
524: server.acceptConnections();
525: Logging.getLogger().info("Server is ready.");
526: }
527: }
|