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 sss.start();
81 }
82
83 public SimpleSocketServer(LoggerContext lc, int port) {
84 this.lc = lc;
85 this.port = port;
86 }
87
88 public void run() {
89
90 final String oldThreadName = Thread.currentThread().getName();
91
92 try {
93
94 final String newThreadName = getServerThreadName();
95 Thread.currentThread().setName(newThreadName);
96
97 logger.info("Listening on port " + port);
98 serverSocket = getServerSocketFactory().createServerSocket(port);
99 while (!closed) {
100 logger.info("Waiting to accept a new client.");
101 signalAlmostReadiness();
102 Socket socket = serverSocket.accept();
103 logger.info("Connected to client at " + socket.getInetAddress());
104 logger.info("Starting new socket node.");
105 SocketNode newSocketNode = new SocketNode(this, socket, lc);
106 synchronized (socketNodeList) {
107 socketNodeList.add(newSocketNode);
108 }
109 final String clientThreadName = getClientThreadName(socket);
110 new Thread(newSocketNode, clientThreadName).start();
111 }
112 } catch (Exception e) {
113 if (closed) {
114 logger.info("Exception in run method for a closed server. This is normal.");
115 } else {
116 logger.error("Unexpected failure in run method", e);
117 }
118 }
119
120 finally {
121 Thread.currentThread().setName(oldThreadName);
122 }
123 }
124
125
126
127
128 protected String getServerThreadName() {
129 return String.format("Logback %s (port %d)", getClass().getSimpleName(), port);
130 }
131
132
133
134
135 protected String getClientThreadName(Socket socket) {
136 return String.format("Logback SocketNode (client: %s)", socket.getRemoteSocketAddress());
137 }
138
139
140
141
142
143
144 protected ServerSocketFactory getServerSocketFactory() {
145 return ServerSocketFactory.getDefault();
146 }
147
148
149
150
151
152 void signalAlmostReadiness() {
153 if (latch != null && latch.getCount() != 0) {
154
155 latch.countDown();
156 }
157 }
158
159
160
161
162
163
164 void setLatch(CountDownLatch latch) {
165 this.latch = latch;
166 }
167
168
169
170
171 public CountDownLatch getLatch() {
172 return latch;
173 }
174
175 public boolean isClosed() {
176 return closed;
177 }
178
179 public void close() {
180 closed = true;
181 if (serverSocket != null) {
182 try {
183 serverSocket.close();
184 } catch (IOException e) {
185 logger.error("Failed to close serverSocket", e);
186 } finally {
187 serverSocket = null;
188 }
189 }
190
191 logger.info("closing this server");
192 synchronized (socketNodeList) {
193 for (SocketNode sn : socketNodeList) {
194 sn.close();
195 }
196 }
197 if (socketNodeList.size() != 0) {
198 logger.warn("Was expecting a 0-sized socketNodeList after server shutdown");
199 }
200
201 }
202
203 public void socketNodeClosing(SocketNode sn) {
204 logger.debug("Removing {}", sn);
205
206
207
208
209 synchronized (socketNodeList) {
210 socketNodeList.remove(sn);
211 }
212 }
213
214 static void usage(String msg) {
215 System.err.println(msg);
216 System.err.println("Usage: java " + SimpleSocketServer.class.getName() + " port configFile");
217 System.exit(1);
218 }
219
220 static int parsePortNumber(String portStr) {
221 try {
222 return Integer.parseInt(portStr);
223 } catch (java.lang.NumberFormatException e) {
224 e.printStackTrace();
225 usage("Could not interpret port number [" + portStr + "].");
226
227 return -1;
228 }
229 }
230
231 static public void configureLC(LoggerContext lc, String configFile) throws JoranException {
232 JoranConfigurator configurator = new JoranConfigurator();
233 lc.reset();
234 configurator.setContext(lc);
235 configurator.doConfigure(configFile);
236 }
237 }