1 /***
2 * Jetrix TetriNET Server
3 * Copyright (C) 2001-2010 Emmanuel Bourg
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package net.jetrix;
21
22 import java.io.*;
23 import java.util.*;
24 import java.util.concurrent.*;
25 import java.util.logging.*;
26
27 import net.jetrix.clients.*;
28 import net.jetrix.commands.*;
29 import net.jetrix.config.*;
30 import net.jetrix.messages.*;
31 import net.jetrix.messages.channel.*;
32 import net.jetrix.services.VersionService;
33 import net.jetrix.listeners.ShutdownListener;
34 import net.jetrix.mail.MailSessionManager;
35
36 /***
37 * Main class, starts the server components and handle the server level messages.
38 *
39 * @author Emmanuel Bourg
40 * @version $Revision: 860 $, $Date: 2010-05-06 13:21:05 +0200 (jeu., 06 mai 2010) $
41 */
42 public class Server implements Runnable, Destination
43 {
44 private static Server instance;
45
46 private Logger log = Logger.getLogger("net.jetrix");
47
48 private File configFile;
49 private ServerConfig config;
50 private BlockingQueue<Message> queue = new LinkedBlockingQueue<Message>();
51 private ChannelManager channelManager;
52 private Client console;
53
54 private Server() { }
55
56 /***
57 * Register the shutdown hooks to stop the server properly when the JVM is stopped.
58 */
59 private void registerHooks()
60 {
61 Thread hook = new Thread("StopHook")
62 {
63 public void run()
64 {
65 if (config != null && config.isRunning())
66 {
67 log.info("Shutdown command received from the system");
68 instance.stop();
69 }
70 }
71 };
72 Runtime.getRuntime().addShutdownHook(hook);
73
74 try
75 {
76 SystemSignal.handle("INT", hook);
77 SystemSignal.handle("TERM", hook);
78 }
79 catch (Throwable e)
80 {
81 log.warning("Unable to hook the system signals: " + e.getMessage());
82 }
83 }
84
85 /***
86 * Return the unique instance of the server.
87 */
88 public static Server getInstance()
89 {
90 if (instance == null)
91 {
92 instance = new Server();
93 }
94
95 return instance;
96 }
97
98 /***
99 * Server initialization.
100 */
101 private void init()
102 {
103 registerHooks();
104
105
106 config = new ServerConfig();
107 config.load(configFile);
108 config.setRunning(true);
109
110
111 LogManager.init();
112
113
114 if (!config.getDataSources().isEmpty())
115 {
116 log.info("Initializing the datasources...");
117 for (DataSourceConfig datasource : config.getDataSources())
118 {
119 DataSourceManager.getInstance().setDataSource(datasource, datasource.getName());
120 }
121 }
122
123 if (config.getMailSessionConfig() != null)
124 {
125 log.info("Initializing the mail session...");
126 MailSessionManager.getInstance().setConfiguration(config.getMailSessionConfig());
127 MailSessionManager.getInstance().checkSession();
128 }
129
130
131 SystrayManager.open();
132
133
134 channelManager = ChannelManager.getInstance();
135 channelManager.clear();
136
137 for (ChannelConfig cc : config.getChannels())
138 {
139 cc.setPersistent(true);
140 channelManager.createChannel(cc);
141 }
142
143
144 for (Listener listener : config.getListeners())
145 {
146 if (listener.isAutoStart())
147 {
148 listener.start();
149 }
150 }
151
152
153 new ShutdownListener().start();
154
155
156 for (Service service : config.getServices())
157 {
158 if (service.isAutoStart())
159 {
160 log.info("Starting service " + service.getName());
161 service.start();
162 }
163 }
164
165
166 VersionService.updateLatestVersion();
167 if (VersionService.isNewVersionAvailable())
168 {
169 log.warning("A new version is available (" + VersionService.getLatestVersion() + "), download it on http://jetrix.sf.net now!");
170 }
171
172
173 console = new ConsoleClient();
174 new Thread(console).start();
175
176 log.info("Server ready!");
177 }
178
179 /***
180 * Start the server.
181 */
182 public void start()
183 {
184 Thread server = new Thread(this, "server");
185 server.start();
186 }
187
188 /***
189 * Stop the server.
190 */
191 public void stop()
192 {
193 config.setRunning(false);
194
195
196 for (Listener listener : config.getListeners())
197 {
198 if (listener.isRunning())
199 {
200 listener.stop();
201 }
202 }
203
204
205 for (Service service : config.getServices())
206 {
207 if (service.isRunning())
208 {
209 service.stop();
210 }
211 }
212
213
214 disconnectAll();
215
216
217 ChannelManager.getInstance().closeAll();
218
219
220 send(new ShutdownMessage());
221 }
222
223 /***
224 * Disconnect all clients from the server.
225 */
226 private void disconnectAll()
227 {
228 ClientRepository repository = ClientRepository.getInstance();
229
230 for (Client client : repository.getClients())
231 {
232 client.disconnect();
233 }
234
235
236 if (console != null)
237 {
238 console.disconnect();
239 }
240 }
241
242 public void run()
243 {
244 init();
245
246 while (config.isRunning())
247 {
248 try
249 {
250
251 Message message = queue.take();
252
253 if (log.isLoggable(Level.FINEST))
254 {
255 log.finest("[server] processing " + message);
256 }
257
258
259
260 if (message instanceof AddPlayerMessage)
261 {
262
263 Client client = ((AddPlayerMessage) message).getClient();
264 int level = client.getUser().getAccessLevel();
265 Channel channel = channelManager.getHomeChannel(level, client.getProtocol().getName());
266
267 if (channel != null)
268 {
269 if (log.isLoggable(Level.FINEST))
270 {
271 log.finest("[server] assigning client to channel " + channel);
272 }
273 channel.send(message);
274 }
275 else
276 {
277
278 if (log.isLoggable(Level.FINEST))
279 {
280 log.finest("[server] no available channels!");
281 }
282 }
283 }
284 else if (message instanceof CommandMessage)
285 {
286 CommandManager.getInstance().execute((CommandMessage) message);
287 }
288 else if (!(message instanceof ShutdownMessage))
289 {
290 log.info("[server] Message not processed " + message);
291 }
292 }
293 catch (InterruptedException e)
294 {
295 log.log(Level.WARNING, e.getMessage(), e);
296 }
297 }
298
299
300 SystrayManager.close();
301 }
302
303 /***
304 * Add a message to the server message queue.
305 */
306 public void send(Message message)
307 {
308 queue.add(message);
309 }
310
311 /***
312 * Return the server configuration.
313 */
314 public ServerConfig getConfig()
315 {
316 return config;
317 }
318
319 /***
320 * Set the server configuration file.
321 */
322 public void setConfigFile(File configFile)
323 {
324 this.configFile = configFile;
325 }
326
327 /***
328 * Server entry point.
329 *
330 * @param args start parameters
331 */
332 public static void main(String[] args)
333 {
334 System.out.println("Jetrix TetriNET Server " + ServerConfig.VERSION + ", Copyright (C) 2001-2010 Emmanuel Bourg\n");
335
336 Server server = Server.getInstance();
337
338 List<String> params = Arrays.asList(args);
339
340
341 int p = params.indexOf("--conf");
342 if (p != -1 && p + 1 < params.size())
343 {
344 server.setConfigFile(new File(params.get(p + 1)));
345 }
346 else
347 {
348 server.setConfigFile(new File("conf/server.xml"));
349 }
350
351 server.start();
352 }
353
354 }