001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016:
017: package org.columba.core.main;
018:
019: import java.io.BufferedReader;
020: import java.io.IOException;
021: import java.io.InputStreamReader;
022: import java.io.PrintWriter;
023: import java.net.ServerSocket;
024: import java.net.Socket;
025: import java.net.SocketException;
026: import java.net.SocketTimeoutException;
027: import java.util.LinkedList;
028: import java.util.List;
029: import java.util.Random;
030: import java.util.StringTokenizer;
031:
032: import javax.swing.JOptionPane;
033:
034: import org.apache.commons.cli.CommandLine;
035: import org.apache.commons.cli.ParseException;
036: import org.columba.core.component.ComponentManager;
037: import org.columba.core.resourceloader.GlobalResourceLoader;
038: import org.columba.core.shutdown.ShutdownManager;
039:
040: /**
041: * Opens a server socket to manage multiple sessions of Columba capable of
042: * passing commands to the main session.
043: * <p>
044: * This class is a singleton because there can only be one server per Columba
045: * session.
046: * <p>
047: * Basic idea taken from www.jext.org (author Roman Guy)
048: *
049: * @author fdietz
050: */
051: public class ColumbaServer {
052: static final String RESOURCE_PATH = "org.columba.core.i18n.dialog";
053:
054: /**
055: * The anonymous user for single-user systems without user name.
056: */
057: protected static final String ANONYMOUS_USER = "anonymous";
058:
059: /**
060: * The singleton instance of this class.
061: */
062: private static ColumbaServer instance;
063:
064: /**
065: * Random number generator for port numbers.
066: */
067: private static Random random = new Random();
068:
069: /**
070: * The port range Columba should use is between LOWEST_PORT and 65536.
071: */
072: private static final int LOWEST_PORT = 30000;
073:
074: /**
075: * Server runs in its own thread.
076: */
077: protected Thread thread;
078:
079: /**
080: * The ServerSocket used by the server.
081: */
082: protected ServerSocket serverSocket;
083:
084: /**
085: * Constructor
086: */
087: protected ColumbaServer() {
088: thread = new Thread(new Runnable() {
089: public void run() {
090: while (!Thread.currentThread().isInterrupted()) {
091: try {
092: handleClient(serverSocket.accept());
093: } catch (SocketTimeoutException ste) {
094: // do nothing here, just continue
095: } catch (IOException ioe) {
096: ioe.printStackTrace();
097:
098: // what to do here? we could start a new server...
099: }
100: }
101:
102: try {
103: serverSocket.close();
104:
105: // cleanup: remove port number file
106: SessionController.serializePortNumber(-1);
107: } catch (IOException ioe) {
108: }
109:
110: serverSocket = null;
111: }
112: }, "ColumbaServer");
113: thread.setDaemon(true);
114:
115: // stop server when shutting down
116: ShutdownManager.getInstance().register(new Runnable() {
117: public void run() {
118: stop();
119: }
120: });
121: }
122:
123: /**
124: * Starts the server.
125: *
126: * @throws IOException
127: */
128: public synchronized void start() throws IOException {
129: if (!isRunning()) {
130: int port;
131: int count = 0;
132:
133: while (serverSocket == null) {
134: // create random port number within range
135: port = random.nextInt(65536 - LOWEST_PORT)
136: + LOWEST_PORT;
137:
138: try {
139: serverSocket = new ServerSocket(port);
140:
141: // store port number in file
142: SessionController.serializePortNumber(port);
143: } catch (SocketException se) { // port is in use, try next
144: count++;
145:
146: if (count == 10) { // something is very wrong here
147: JOptionPane.showMessageDialog(null,
148: GlobalResourceLoader.getString(
149: RESOURCE_PATH, "session",
150: "err_10se_msg"),
151: GlobalResourceLoader.getString(
152: RESOURCE_PATH, "session",
153: "err_10se_title"),
154: JOptionPane.ERROR_MESSAGE);
155:
156: // this is save because the only shutdown plugin
157: // to stop this server, the configuration isn't touched
158: System.exit(1);
159: }
160: }
161: }
162:
163: serverSocket.setSoTimeout(2000);
164: thread.start();
165: }
166: }
167:
168: /**
169: * Stops the server.
170: */
171: public synchronized void stop() {
172: thread.interrupt();
173: }
174:
175: /**
176: * Check if server is already running
177: *
178: * @return true, if server is running. False, otherwise
179: */
180: public synchronized boolean isRunning() {
181: return thread.isAlive();
182: }
183:
184: /**
185: * Handles a client connect and authentication.
186: */
187: protected void handleClient(Socket client) {
188: try {
189: // only accept client from local machine
190: String host = client.getLocalAddress().getHostAddress();
191: if (!(host.equals("127.0.0.1"))) {
192: // client isn't from local machine
193: return;
194: }
195:
196: BufferedReader reader = new BufferedReader(
197: new InputStreamReader(client.getInputStream()));
198: String line = reader.readLine();
199: if (!line.startsWith("Columba ")) {
200: return;
201: }
202:
203: line = reader.readLine();
204: if (!line.startsWith("User ")) {
205: return;
206: }
207:
208: PrintWriter writer = new PrintWriter(client
209: .getOutputStream());
210: if (!line.substring(5).equals(
211: System.getProperty("user.name", ANONYMOUS_USER))) {
212: writer.write("WRONG USER\r\n");
213: writer.close();
214: return;
215: }
216: writer.write("\r\n");
217: writer.flush();
218:
219: line = reader.readLine();
220: // do something with the arguments..
221: List<String> list = new LinkedList<String>();
222: StringTokenizer st = new StringTokenizer(line, "%");
223: while (st.hasMoreTokens()) {
224: String tok = (String) st.nextToken();
225: list.add(tok);
226: }
227:
228: try {
229: CommandLine commandLine = ColumbaCmdLineParser
230: .getInstance().parse(
231: (String[]) list.toArray(new String[0]));
232:
233: ComponentManager.getInstance()
234: .handleCommandLineParameters(commandLine);
235:
236: } catch (ParseException e) {
237: e.printStackTrace();
238: }
239: } catch (IOException ioe) {
240: ioe.printStackTrace();
241: } finally {
242: try {
243: client.close();
244: } catch (IOException ioe) {
245: }
246: }
247: }
248:
249: /**
250: * Returns the singleton instance of this class.
251: *
252: * @return instance
253: */
254: public static synchronized ColumbaServer getColumbaServer() {
255: if (instance == null) {
256: instance = new ColumbaServer();
257: }
258: return instance;
259: }
260: }
|