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