View Javadoc

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         // read the server configuration
106         config = new ServerConfig();
107         config.load(configFile);
108         config.setRunning(true);
109 
110         // prepare the loggers
111         LogManager.init();
112         
113         // open the database connections
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         // display the systray icon (windows only)
131         SystrayManager.open();
132 
133         // spawning persistent channels
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         // start the client listeners
144         for (Listener listener : config.getListeners())
145         {
146             if (listener.isAutoStart())
147             {
148                 listener.start();
149             }
150         }
151 
152         // start the shutdown listener
153         new ShutdownListener().start();
154 
155         // start the services
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         // check the availability of a new release
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         // start the server console
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         // stop the listeners
196         for (Listener listener : config.getListeners())
197         {
198             if (listener.isRunning())
199             {
200                 listener.stop();
201             }
202         }
203 
204         // stop the services
205         for (Service service : config.getServices())
206         {
207             if (service.isRunning())
208             {
209                 service.stop();
210             }
211         }
212 
213         // disconnect all clients
214         disconnectAll();
215 
216         // close the channels
217         ChannelManager.getInstance().closeAll();
218 
219         // stop the server thread
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         // disconnect the console client as well
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                 // fetching next message waiting in the queue
251                 Message message = queue.take();
252 
253                 if (log.isLoggable(Level.FINEST))
254                 {
255                     log.finest("[server] processing " + message);
256                 }
257 
258                 // processing message
259 
260                 if (message instanceof AddPlayerMessage)
261                 {
262                     // look for a suitable channel
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                         // send server full message or create a new channel
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         // remove the system tray icon
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         // read the path of the server configuration file
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 }