1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.daemon;
19
20 import java.io.*;
21 import java.net.*;
22 import java.text.SimpleDateFormat;
23 import java.util.Date;
24 import java.util.Enumeration;
25 import java.util.Vector;
26
27 public class SimpleDaemon implements Daemon, Runnable, DaemonUserSignal {
28
29 private ServerSocket server;
30 private Thread thread;
31 private DaemonController controller;
32 private volatile boolean stopping;
33 private String directory;
34 private final Vector<Handler> handlers;
35 private boolean softReloadSignalled;
36
37 public SimpleDaemon() {
38 System.err.println("SimpleDaemon: instance " + hashCode() + " created");
39 this.handlers = new Vector<>();
40 }
41
42 @Override
43 protected void finalize() {
44 System.err.println("SimpleDaemon: instance " + hashCode() + " garbage collected");
45 }
46
47
48
49
50 @Override
51 public void init(final DaemonContext context) throws Exception {
52 System.err.println("SimpleDaemon: instance " + hashCode() + " init");
53
54 int port = 1200;
55
56 final String[] a = context.getArguments();
57
58 if (a.length > 0) {
59 port = Integer.parseInt(a[0]);
60 }
61 if (a.length > 1) {
62 this.directory = a[1];
63 } else {
64 this.directory = "/tmp";
65 }
66
67
68 System.err.println("SimpleDaemon: loading on port " + port);
69
70
71 this.controller = context.getController();
72 this.server = new ServerSocket(port);
73 this.thread = new Thread(this);
74 }
75
76 @Override
77 public void start() {
78
79 System.err.println("SimpleDaemon: starting");
80
81
82 this.thread.start();
83 }
84
85 @Override
86 public void stop() throws IOException, InterruptedException {
87
88 System.err.println("SimpleDaemon: stopping");
89
90
91 this.stopping = true;
92 this.server.close();
93
94
95 this.thread.join(5000);
96 System.err.println("SimpleDaemon: stopped");
97 }
98
99 @Override
100 public void destroy() {
101 System.err.println("SimpleDaemon: instance " + hashCode() + " destroy");
102 }
103
104 @Override
105 public void run() {
106 int number = 0;
107
108 System.err.println("SimpleDaemon: started acceptor loop");
109 try {
110 while (!this.stopping) {
111 checkForReload();
112 final Socket socket = this.server.accept();
113 checkForReload();
114
115 final Handler handler = new Handler(socket, this, this.controller);
116 handler.setConnectionNumber(number++);
117 handler.setDirectoryName(this.directory);
118 new Thread(handler).start();
119 }
120 } catch (final IOException e) {
121
122
123
124 if (!this.stopping) {
125 e.printStackTrace(System.err);
126 }
127 }
128
129
130 final Enumeration<Handler> openhandlers = this.handlers.elements();
131 while (openhandlers.hasMoreElements()) {
132 final Handler handler = openhandlers.nextElement();
133 System.err.println("SimpleDaemon: dropping connection " + handler.getConnectionNumber());
134 handler.close();
135 }
136
137 System.err.println("SimpleDaemon: exiting acceptor loop");
138 }
139
140 @Override
141 public void signal() {
142
143
144
145 this.softReloadSignalled = true;
146 }
147
148 private void checkForReload() {
149 if (this.softReloadSignalled) {
150 System.err.println("SimpleDaemon: picked up reload, waiting for connections to finish...");
151 while (!this.handlers.isEmpty()) {
152
153 }
154 System.err.println("SimpleDaemon: all connections have finished, pretending to reload");
155 this.softReloadSignalled = false;
156 }
157 }
158
159 protected void addHandler(final Handler handler) {
160 synchronized (handler) {
161 this.handlers.add(handler);
162 }
163 }
164
165 protected void removeHandler(final Handler handler) {
166 synchronized (handler) {
167 this.handlers.remove(handler);
168 }
169 }
170
171 public static class Handler implements Runnable {
172
173 private final DaemonController controller;
174 private final SimpleDaemon parent;
175 private String directory;
176 private final Socket socket;
177 private int number;
178
179 public Handler(final Socket s, final SimpleDaemon p, final DaemonController c) {
180 this.socket = s;
181 this.parent = p;
182 this.controller = c;
183 }
184
185 @Override
186 public void run() {
187 this.parent.addHandler(this);
188 System.err.println("SimpleDaemon: connection " + this.number + " opened from " + this.socket.getInetAddress());
189 try {
190 final InputStream in = this.socket.getInputStream();
191 final OutputStream out = this.socket.getOutputStream();
192 handle(in, out);
193 this.socket.close();
194 } catch (final IOException e) {
195 e.printStackTrace(System.err);
196 }
197 System.err.println("SimpleDaemon: connection " + this.number + " closed");
198 this.parent.removeHandler(this);
199 }
200
201 public void close() {
202 try {
203 this.socket.close();
204 } catch (final IOException e) {
205 e.printStackTrace(System.err);
206 }
207 }
208
209 public void setConnectionNumber(final int number) {
210 this.number = number;
211 }
212
213 public int getConnectionNumber() {
214 return this.number;
215 }
216
217 public void setDirectoryName(final String directory) {
218 this.directory = directory;
219 }
220
221 public String getDirectoryName() {
222 return this.directory;
223 }
224
225 public void log(final String name) throws IOException {
226 try (final OutputStream file = new FileOutputStream(name, true);
227 final PrintStream out = new PrintStream(file)) {
228 out.println(new SimpleDateFormat().format(new Date()));
229 }
230 }
231
232 public void handle(final InputStream in, final OutputStream os) {
233 final PrintStream out = new PrintStream(os);
234
235 while (true) {
236 try {
237
238
239
240 if (in.available() == 0) {
241 out.println();
242 out.println("Please select one of the following:");
243 out.println(" 1) Shutdown");
244 out.println(" 2) Reload");
245 out.println(" 3) Create a file");
246 out.println(" 4) Disconnect");
247 out.println(" 5) Soft reload");
248 out.print("Your choice: ");
249 }
250
251
252 final int x = in.read();
253
254 switch (x) {
255
256 case -1:
257 return;
258
259
260 case '1':
261 out.println("Attempting a shutdown...");
262 try {
263 this.controller.shutdown();
264 } catch (final IllegalStateException e) {
265 out.println();
266 out.println("Can't shutdown now");
267 e.printStackTrace(out);
268 }
269 break;
270
271
272 case '2':
273 out.println("Attempting a reload...");
274 try {
275 this.controller.reload();
276 } catch (final IllegalStateException e) {
277 out.println();
278 out.println("Can't reload now");
279 e.printStackTrace(out);
280 }
281 break;
282
283
284 case '3':
285 final String name = getDirectoryName() + "/SimpleDaemon." + getConnectionNumber() + ".tmp";
286 try {
287 log(name);
288 out.println("File '" + name + "' created");
289 } catch (final IOException e) {
290 e.printStackTrace(out);
291 }
292 break;
293
294
295 case '4':
296 out.println("Disconnecting...");
297 return;
298 case '5':
299 out.println("Reloading configuration...");
300 this.parent.signal();
301 return;
302
303
304 case '\r':
305 case '\n':
306 break;
307
308
309 default:
310 out.println("Unknown option '" + (char) x + "'");
311 break;
312
313 }
314
315
316 } catch (final IOException e) {
317 System.err.println("SimpleDaemon: IOException in " + "connection " + getConnectionNumber());
318 return;
319 }
320 }
321 }
322 }
323 }