1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.classic.net;
15
16 import java.io.IOException;
17 import java.net.ServerSocket;
18 import java.net.Socket;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.CountDownLatch;
22
23 import javax.net.ServerSocketFactory;
24
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import ch.qos.logback.classic.LoggerContext;
29 import ch.qos.logback.classic.joran.JoranConfigurator;
30 import ch.qos.logback.core.joran.spi.JoranException;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class SimpleSocketServer extends Thread {
51
52 Logger logger = LoggerFactory.getLogger(SimpleSocketServer.class);
53
54 private final int port;
55 private final LoggerContext lc;
56 private boolean closed = false;
57 private ServerSocket serverSocket;
58 private List<SocketNode> socketNodeList = new ArrayList<SocketNode>();
59
60
61 private CountDownLatch latch;
62
63 public static void main(String argv[]) throws Exception {
64 doMain(SimpleSocketServer.class, argv);
65 }
66
67 protected static void doMain(Class<? extends SimpleSocketServer> serverClass, String argv[]) throws Exception {
68 int port = -1;
69 if (argv.length == 2) {
70 port = parsePortNumber(argv[0]);
71 } else {
72 usage("Wrong number of arguments.");
73 }
74
75 String configFile = argv[1];
76 LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
77 configureLC(lc, configFile);
78
79 SimpleSocketServer sss = new SimpleSocketServer(lc, port);
80
81
82 sss.start();
83 }
84
85 public SimpleSocketServer(LoggerContext lc, int port) {
86 this.lc = lc;
87 this.port = port;
88 }
89
90 public void run() {
91
92 final String oldThreadName = Thread.currentThread().getName();
93
94 try {
95
96 final String newThreadName = getServerThreadName();
97 Thread.currentThread().setName(newThreadName);
98
99 logger.info("Listening on port " + port);
100 serverSocket = getServerSocketFactory().createServerSocket(port);
101 while (!closed) {
102 logger.info("Waiting to accept a new client.");
103 signalAlmostReadiness();
104 Socket socket = serverSocket.accept();
105 logger.info("Connected to client at " + socket.getInetAddress());
106 logger.info("Starting new socket node.");
107 SocketNode newSocketNode = new SocketNode(this, socket, lc);
108 synchronized (socketNodeList) {
109 socketNodeList.add(newSocketNode);
110 }
111 final String clientThreadName = getClientThreadName(socket);
112 new Thread(newSocketNode, clientThreadName).start();
113 }
114 } catch (Exception e) {
115 if (closed) {
116 logger.info("Exception in run method for a closed server. This is normal.");
117 } else {
118 logger.error("Unexpected failure in run method", e);
119 }
120 }
121
122 finally {
123 Thread.currentThread().setName(oldThreadName);
124 }
125 }
126
127
128
129
130 protected String getServerThreadName() {
131 return String.format("Logback %s (port %d)", getClass().getSimpleName(), port);
132 }
133
134
135
136
137 protected String getClientThreadName(Socket socket) {
138 return String.format("Logback SocketNode (client: %s)", socket.getRemoteSocketAddress());
139 }
140
141
142
143
144
145
146 protected ServerSocketFactory getServerSocketFactory() {
147 return ServerSocketFactory.getDefault();
148 }
149
150
151
152
153
154 void signalAlmostReadiness() {
155 if (latch != null && latch.getCount() != 0) {
156
157 latch.countDown();
158 }
159 }
160
161
162
163
164
165
166 void setLatch(CountDownLatch latch) {
167 this.latch = latch;
168 }
169
170
171
172
173 public CountDownLatch getLatch() {
174 return latch;
175 }
176
177 public boolean isClosed() {
178 return closed;
179 }
180
181 public void close() {
182 closed = true;
183 if (serverSocket != null) {
184 try {
185 serverSocket.close();
186 } catch (IOException e) {
187 logger.error("Failed to close serverSocket", e);
188 } finally {
189 serverSocket = null;
190 }
191 }
192
193 logger.info("closing this server");
194 synchronized (socketNodeList) {
195 for (SocketNode sn : socketNodeList) {
196 sn.close();
197 }
198 }
199 if (socketNodeList.size() != 0) {
200 logger.warn("Was expecting a 0-sized socketNodeList after server shutdown");
201 }
202
203 }
204
205 public void socketNodeClosing(SocketNode sn) {
206 logger.debug("Removing {}", sn);
207
208
209
210
211 synchronized (socketNodeList) {
212 socketNodeList.remove(sn);
213 }
214 }
215
216 static void usage(String msg) {
217 System.err.println(msg);
218 System.err.println("Usage: java " + SimpleSocketServer.class.getName() + " port configFile");
219 System.exit(1);
220 }
221
222 static int parsePortNumber(String portStr) {
223 try {
224 return Integer.parseInt(portStr);
225 } catch (java.lang.NumberFormatException e) {
226 e.printStackTrace();
227 usage("Could not interpret port number [" + portStr + "].");
228
229 return -1;
230 }
231 }
232
233 static public void configureLC(LoggerContext lc, String configFile) throws JoranException {
234 JoranConfigurator configurator = new JoranConfigurator();
235 lc.reset();
236 configurator.setContext(lc);
237 configurator.doConfigure(configFile);
238 }
239 }