View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  package ch.qos.logback.classic.net;
11  
12  import java.io.IOException;
13  import java.net.ServerSocket;
14  import java.net.Socket;
15  import java.util.ArrayList;
16  import java.util.List;
17  
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  import ch.qos.logback.classic.LoggerContext;
22  import ch.qos.logback.classic.joran.JoranConfigurator;
23  import ch.qos.logback.core.joran.spi.JoranException;
24  
25  /**
26   * A simple {@link SocketNode} based server.
27   * 
28   * <pre>
29   *      &lt;b&gt;Usage:&lt;/b&gt; java ch.qos.logback.classic.net.SimpleSocketServer port configFile
30   * </pre>
31   * 
32   * where <em>port</em> is a port number where the server listens and
33   * <em>configFile</em> is an xml configuration file fed to
34   * {@link JoranConfigurator}.
35   * 
36   * </pre>
37   * 
38   * @author Ceki G&uuml;lc&uuml;
39   * @author S&eacute;bastien Pennec
40   * 
41   * @since 0.8.4
42   */
43  public class SimpleSocketServer extends Thread {
44  
45    Logger logger = LoggerFactory.getLogger(SimpleSocketServer.class);
46  
47    private final int port;
48    private final LoggerContext lc;
49    private boolean closed = false;
50    private ServerSocket serverSocket;
51    private List<SocketNode> socketNodeList = new ArrayList<SocketNode>();
52    
53    public static void main(String argv[]) throws Exception {
54      int port = -1;
55      if (argv.length == 2) {
56        port = parsePortNumber(argv[0]);
57      } else {
58        usage("Wrong number of arguments.");
59      }
60  
61      String configFile = argv[1];
62      LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
63      configureLC(lc, configFile);
64  
65      SimpleSocketServer sss = new SimpleSocketServer(lc, port);
66      sss.start();
67    }
68  
69    public SimpleSocketServer(LoggerContext lc, int port) {
70      this.lc = lc;
71      this.port = port;
72    }
73  
74    public void run() {
75      try {
76        logger.info("Listening on port " + port);
77        serverSocket = new ServerSocket(port);
78        while (!closed) {
79          logger.info("Waiting to accept a new client.");
80          Socket socket = serverSocket.accept();
81          logger.info("Connected to client at " + socket.getInetAddress());
82          logger.info("Starting new socket node.");
83          SocketNode newSocketNode = new SocketNode(this, socket, lc); 
84          synchronized (socketNodeList) {
85            socketNodeList.add(newSocketNode);
86          }
87          new Thread(newSocketNode).start();
88          signalSocketNodeCreation();
89        }
90      } catch (Exception e) {
91        if(closed) {
92          logger.info("Exception in run method for a closed server. This is normal.");
93        } else {
94          logger.error("Unexpected failure in run method", e);
95        }
96      }
97    }
98  
99    /**
100    * Signal another thread that we have established a conneciton
101    *  This is useful for testing purposes.
102    */
103   void signalSocketNodeCreation() {
104     synchronized(this) {
105       this.notifyAll();
106     }
107   }
108   public boolean isClosed() {
109     return closed;
110   }
111 
112   public void close() {
113     closed = true;
114     if (serverSocket != null) {
115       try {
116         serverSocket.close();
117       } catch (IOException e) {
118         logger.error("Failed to close serverSocket", e);
119       } finally {
120         serverSocket = null;
121       }
122     } 
123     
124     synchronized (socketNodeList) {
125       for(SocketNode sn: socketNodeList) {
126         sn.close();
127       }      
128       socketNodeList.clear();
129     }
130   }
131 
132   public void socketNodeClosing(SocketNode sn) {
133     logger.debug("Removing {}", sn);
134 
135     // don't allow simultaneous access to the socketNodeList
136     // (e.g. removal whole iterating on the list causes
137     // java.util.ConcurrentModificationException
138     synchronized (socketNodeList) {
139       socketNodeList.remove(sn);      
140     }
141   }
142   static void usage(String msg) {
143     System.err.println(msg);
144     System.err.println("Usage: java " + SimpleSocketServer.class.getName()
145         + " port configFile");
146     System.exit(1);
147   }
148 
149   static int parsePortNumber(String portStr) {
150     try {
151       return Integer.parseInt(portStr);
152     } catch (java.lang.NumberFormatException e) {
153       e.printStackTrace();
154       usage("Could not interpret port number [" + portStr + "].");
155       // we won't get here
156       return -1;
157     }
158   }
159 
160   static public void configureLC(LoggerContext lc, String configFile)
161       throws JoranException {
162     JoranConfigurator configurator = new JoranConfigurator();
163     lc.reset();
164     configurator.setContext(lc);
165     configurator.doConfigure(configFile);
166   }
167 }