# Coverage details for com.martiansoftware.nailgun.NGServer

Line | Hits | Source
--- | --- | ---
1 |  | `/*`
2 |  | ``
3 |  | `Copyright 2004, Martian Software, Inc.`
4 |  | ``
5 |  | `Licensed under the Apache License, Version 2.0 (the "License");`
6 |  | `you may not use this file except in compliance with the License.`
7 |  | `You may obtain a copy of the License at`
8 |  | ``
9 |  | `http://www.apache.org/licenses/LICENSE-2.0`
10 |  | ``
11 |  | `Unless required by applicable law or agreed to in writing, software`
12 |  | `distributed under the License is distributed on an "AS IS" BASIS,`
13 |  | `WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.`
14 |  | `See the License for the specific language governing permissions and`
15 |  | `limitations under the License.`
16 |  | ``
17 |  | `*/`
18 |  | ``
19 |  | `package com.martiansoftware.nailgun;`
20 |  | `import java.io.InputStream;`
21 |  | `import java.io.PrintStream;`
22 |  | `import java.lang.reflect.Method;`
23 |  | `import java.net.InetAddress;`
24 |  | `import java.net.ServerSocket;`
25 |  | `import java.net.Socket;`
26 |  | `import java.net.UnknownHostException;`
27 |  | `import java.util.Iterator;`
28 |  | `import java.util.Map;`
29 |  | ``
30 |  | `import com.martiansoftware.nailgun.builtins.DefaultNail;`
31 |  | ``
32 |  | `/**`
33 |  | `* <p>Listens for new connections from NailGun clients and launches`
34 |  | `* NGSession threads to process them.</p>`
35 |  | `*`
36 |  | `* <p>This class can be run as a standalone server or can be embedded`
37 |  | `* within larger applications as a means of providing command-line`
38 |  | `* interaction with the application.</p>`
39 |  | `*`
40 |  | `* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>`
41 |  | `*/`
42 |  | `public class NGServer implements Runnable {`
43 |  | ``
44 |  | `/**`
45 |  | `* The address on which to listen, or null to listen on all`
46 |  | `* local addresses`
47 |  | `*/`
48 | 1 | `private InetAddress addr = null;`
49 |  | ``
50 |  | `/**`
51 |  | `* The port on which to listen, or zero to select a port automatically`
52 |  | `*/`
53 | 1 | `private int port = 0;`
54 |  | ``
55 |  | `/**`
56 |  | `* The socket doing the listening`
57 |  | `*/`
58 |  | `private ServerSocket serversocket;`
59 |  | ``
60 |  | `/**`
61 |  | `* True if this NGServer has received instructions to shut down`
62 |  | `*/`
63 | 1 | `private boolean shutdown = false;`
64 |  | ``
65 |  | `/**`
66 |  | `* True if this NGServer has been started and is accepting connections`
67 |  | `*/`
68 | 1 | `private boolean running = false;`
69 |  | ``
70 |  | `/**`
71 |  | `* This NGServer's AliasManager, which maps aliases to classes`
72 |  | `*/`
73 |  | `private AliasManager aliasManager;`
74 |  | ``
75 |  | `/**`
76 |  | `* If true, fully-qualified classnames are valid commands`
77 |  | `*/`
78 | 1 | `private boolean allowNailsByClassName = true;`
79 |  | ``
80 |  | `/**`
81 |  | `* The default class to use if an invalid alias or classname is`
82 |  | `* specified by the client.`
83 |  | `*/`
84 | 1 | `private Class defaultNailClass = null;`
85 |  | ``
86 |  | `/**`
87 |  | `* A pool of NGSessions ready to handle client connections`
88 |  | `*/`
89 | 1 | `private NGSessionPool sessionPool = null;`
90 |  | ``
91 |  | `/**`
92 |  | `* <code>System.out</code> at the time of the NGServer's creation`
93 |  | `*/`
94 | 1 | `public final PrintStream out = System.out;`
95 |  | ``
96 |  | `/**`
97 |  | `* <code>System.err</code> at the time of the NGServer's creation`
98 |  | `*/`
99 | 1 | `public final PrintStream err = System.err;`
100 |  | ``
101 |  | `/**`
102 |  | `* <code>System.in</code> at the time of the NGServer's creation`
103 |  | `*/`
104 | 1 | `public final InputStream in = System.in;`
105 |  | ``
106 |  | `/**`
107 |  | `* a collection of all classes executed by this server so far`
108 |  | `*/`
109 | 1 | `private Map allNailStats = null;`
110 |  | ``
111 |  | `/**`
112 |  | `* Remember the security manager we start with so we can restore it later`
113 |  | `*/`
114 | 1 | `private SecurityManager originalSecurityManager = null;`
115 |  | ``
116 |  | `/**`
117 |  | `* Creates a new NGServer that will listen at the specified address and`
118 |  | `* on the specified port.`
119 |  | `* This does <b>not</b> cause the server to start listening. To do`
120 |  | `* so, create a new <code>Thread</code> wrapping this <code>NGServer</code>`
121 |  | `* and start it.`
122 |  | `* @param addr the address at which to listen, or <code>null</code> to bind`
123 |  | `* to all local addresses`
124 |  | `* @param port the port on which to listen.`
125 |  | `*/`
126 | 0 | `public NGServer(InetAddress addr, int port) {`
127 | 0 | `init(addr, port);`
128 | 0 | `}`
129 |  | ``
130 |  | `/**`
131 |  | `* Creates a new NGServer that will listen on the default port`
132 |  | `* (defined in <code>NGConstants.DEFAULT_PORT</code>).`
133 |  | `* This does <b>not</b> cause the server to start listening. To do`
134 |  | `* so, create a new <code>Thread</code> wrapping this <code>NGServer</code>`
135 |  | `* and start it.`
136 |  | `*/`
137 | 1 | `public NGServer() {`
138 | 1 | `init(null, NGConstants.DEFAULT_PORT);`
139 | 1 | `}`
140 |  | ``
141 |  | `/**`
142 |  | `* Sets up the NGServer internals`
143 |  | `* @param addr the InetAddress to bind to`
144 |  | `* @param port the port on which to listen`
145 |  | `*/`
146 |  | `private void init(InetAddress addr, int port) {`
147 | 1 | `this.addr = addr;`
148 | 1 | `this.port = port;`
149 |  | ``
150 | 1 | `this.aliasManager = new AliasManager();`
151 | 1 | `allNailStats = new java.util.HashMap();`
152 |  | `// allow a maximum of 10 idle threads. probably too high a number`
153 |  | `// and definitely should be configurable in the future`
154 | 1 | `sessionPool = new NGSessionPool(this, 10);`
155 | 1 | `}`
156 |  | ``
157 |  | `/**`
158 |  | `* Sets a flag that determines whether Nails can be executed by class name.`
159 |  | `* If this is false, Nails can only be run via aliases (and you should`
160 |  | `* probably remove ng-alias from the AliasManager).`
161 |  | `*`
162 |  | `* @param allowNailsByClassName true iff Nail lookups by classname are allowed`
163 |  | `*/`
164 |  | `public void setAllowNailsByClassName(boolean allowNailsByClassName) {`
165 | 0 | `this.allowNailsByClassName = allowNailsByClassName;`
166 | 0 | `}`
167 |  | ``
168 |  | `/**`
169 |  | `* Returns a flag that indicates whether Nail lookups by classname`
170 |  | `* are allowed. If this is false, Nails can only be run via aliases.`
171 |  | `* @return a flag that indicates whether Nail lookups by classname`
172 |  | `* are allowed.`
173 |  | `*/`
174 |  | `public boolean allowsNailsByClassName() {`
175 | 0 | `return (allowNailsByClassName);`
176 |  | `}`
177 |  | ``
178 |  | `/**`
179 |  | `* Sets the default class to use for the Nail if no Nails can`
180 |  | `* be found via alias or classname. (may be <code>null</code>,`
181 |  | `* in which case NailGun will use its own default)`
182 |  | `* @param defaultNailClass the default class to use for the Nail`
183 |  | `* if no Nails can be found via alias or classname.`
184 |  | `* (may be <code>null</code>, in which case NailGun will use`
185 |  | `* its own default)`
186 |  | `*/`
187 |  | `public void setDefaultNailClass(Class defaultNailClass) {`
188 | 0 | `this.defaultNailClass = defaultNailClass;`
189 | 0 | `}`
190 |  | ``
191 |  | `/**`
192 |  | `* Returns the default class that will be used if no Nails`
193 |  | `* can be found via alias or classname.`
194 |  | `* @return the default class that will be used if no Nails`
195 |  | `* can be found via alias or classname.`
196 |  | `*/`
197 |  | `public Class getDefaultNailClass() {`
198 | 0 | `return ((defaultNailClass == null) ? DefaultNail.class : defaultNailClass) ;`
199 |  | `}`
200 |  | ``
201 |  | `/**`
202 |  | `* Returns the current NailStats object for the specified class, creating`
203 |  | `* a new one if necessary`
204 |  | `* @param nailClass the class for which we're gathering stats`
205 |  | `* @return a NailStats object for the specified class`
206 |  | `*/`
207 |  | `private NailStats getOrCreateStatsFor(Class nailClass) {`
208 | 0 | `NailStats result = null;`
209 | 0 | `synchronized(allNailStats) {`
210 | 0 | `result = (NailStats) allNailStats.get(nailClass);`
211 | 0 | `if (result == null) {`
212 | 0 | `result = new NailStats(nailClass);`
213 | 0 | `allNailStats.put(nailClass, result);`
214 |  | `}`
215 | 0 | `}`
216 | 0 | `return (result);`
217 |  | `}`
218 |  | ``
219 |  | `/**`
220 |  | `* Provides a means for an NGSession to register the starting of`
221 |  | `* a nail execution with the server.`
222 |  | `*`
223 |  | `* @param nailClass the nail class that was launched`
224 |  | `*/`
225 |  | `void nailStarted(Class nailClass) {`
226 | 0 | `NailStats stats = getOrCreateStatsFor(nailClass);`
227 | 0 | `stats.nailStarted();`
228 | 0 | `}`
229 |  | ``
230 |  | `/**`
231 |  | `* Provides a means for an NGSession to register the completion of`
232 |  | `* a nails execution with the server.`
233 |  | `*`
234 |  | `* @param nailClass the nail class that finished`
235 |  | `*/`
236 |  | `void nailFinished(Class nailClass) {`
237 | 0 | `NailStats stats = (NailStats) allNailStats.get(nailClass);`
238 | 0 | `stats.nailFinished();`
239 | 0 | `}`
240 |  | ``
241 |  | `/**`
242 |  | `* Returns a snapshot of this NGServer's nail statistics. The result is a <code>java.util.Map</code>,`
243 |  | `* keyed by class name, with <a href="NailStats.html">NailStats</a> objects as values.`
244 |  | `*`
245 |  | `* @return a snapshot of this NGServer's nail statistics.`
246 |  | `*/`
247 |  | `public Map getNailStats() {`
248 | 0 | `Map result = new java.util.TreeMap();`
249 | 0 | `synchronized(allNailStats) {`
250 | 0 | `for (Iterator i = allNailStats.keySet().iterator(); i.hasNext();) {`
251 | 0 | `Class nailclass = (Class) i.next();`
252 | 0 | `result.put(nailclass.getName(), ((NailStats) allNailStats.get(nailclass)).clone());`
253 |  | `}`
254 | 0 | `}`
255 | 0 | `return (result);`
256 |  | `}`
257 |  | ``
258 |  | `/**`
259 |  | `* Returns the AliasManager in use by this NGServer.`
260 |  | `* @return the AliasManager in use by this NGServer.`
261 |  | `*/`
262 |  | `public AliasManager getAliasManager() {`
263 | 0 | `return (aliasManager);`
264 |  | `}`
265 |  | ``
266 |  | `/**`
267 |  | `* <p>Shuts down the server. The server will stop listening`
268 |  | `* and its thread will finish. Any running nails will be allowed`
269 |  | `* to finish.</p>`
270 |  | `*`
271 |  | `* <p>Any nails that provide a`
272 |  | `* <pre><code>public static void nailShutdown(NGServer)</code></pre>`
273 |  | `* method will have this method called with this NGServer as its sole`
274 |  | `* parameter.</p>`
275 |  | `*`
276 |  | `* @param exitVM if true, this method will also exit the JVM after`
277 |  | `* calling nailShutdown() on any nails. This may prevent currently`
278 |  | `* running nails from exiting gracefully, but may be necessary in order`
279 |  | `* to perform some tasks, such as shutting down any AWT or Swing threads`
280 |  | `* implicitly launched by your nails.`
281 |  | `*/`
282 |  | `public void shutdown(boolean exitVM) {`
283 | 0 | `synchronized(this) {`
284 | 0 | `if (shutdown) return;`
285 | 0 | `shutdown = true;`
286 | 0 | `}`
287 |  | ``
288 |  | `try {`
289 | 0 | `serversocket.close();`
290 | 0 | `} catch (Throwable toDiscard) {}`
291 |  | ``
292 | 0 | `sessionPool.shutdown();`
293 |  | ``
294 | 0 | `Class[] argTypes = new Class[1];`
295 | 0 | `argTypes[0] = NGServer.class;`
296 | 0 | `Object[] argValues = new Object[1];`
297 | 0 | `argValues[0] = this;`
298 |  | ``
299 |  | `// make sure that all aliased classes have associated nailstats`
300 |  | `// so they can be shut down.`
301 | 0 | `for (Iterator i = getAliasManager().getAliases().iterator(); i.hasNext();) {`
302 | 0 | `Alias alias = (Alias) i.next();`
303 | 0 | `getOrCreateStatsFor(alias.getAliasedClass());`
304 |  | `}`
305 |  | ``
306 | 0 | `synchronized(allNailStats) {`
307 | 0 | `for (Iterator i = allNailStats.values().iterator(); i.hasNext();) {`
308 | 0 | `NailStats ns = (NailStats) i.next();`
309 | 0 | `Class nailClass = ns.getNailClass();`
310 |  | ``
311 |  | `// yes, I know this is lazy, relying upon the exception`
312 |  | `// to handle the case of no nailShutdown method.`
313 |  | `try {`
314 | 0 | `Method nailShutdown = nailClass.getMethod("nailShutdown", argTypes);`
315 | 0 | `nailShutdown.invoke(null, argValues);`
316 | 0 | `} catch (Throwable toDiscard) {}`
317 |  | `}`
318 | 0 | `}`
319 |  | ``
320 |  | `// restore system streams`
321 | 0 | `System.setIn(in);`
322 | 0 | `System.setOut(out);`
323 | 0 | `System.setErr(err);`
324 |  | ``
325 | 0 | `System.setSecurityManager(originalSecurityManager);`
326 |  | ``
327 | 0 | `if (exitVM) {`
328 | 0 | `System.exit(0);`
329 |  | `}`
330 | 0 | `}`
331 |  | ``
332 |  | `/**`
333 |  | `* Returns true iff the server is currently running.`
334 |  | `* @return true iff the server is currently running.`
335 |  | `*/`
336 |  | `public boolean isRunning() {`
337 | 0 | `return (running);`
338 |  | `}`
339 |  | ``
340 |  | `/**`
341 |  | `* Returns the port on which this server is (or will be) listening.`
342 |  | `* @return the port on which this server is (or will be) listening.`
343 |  | `*/`
344 |  | `public int getPort() {`
345 | 0 | `return ((serversocket == null) ? port : serversocket.getLocalPort());`
346 |  | `}`
347 |  | ``
348 |  | `/**`
349 |  | `* Listens for new connections and launches NGSession threads`
350 |  | `* to process them.`
351 |  | `*/`
352 |  | `public void run() {`
353 | 0 | `running = true;`
354 | 0 | `NGSession sessionOnDeck = null;`
355 |  | ``
356 | 0 | `originalSecurityManager = System.getSecurityManager();`
357 | 0 | `System.setSecurityManager(`
358 |  | `new NGSecurityManager(`
359 |  | `originalSecurityManager));`
360 |  | ``
361 |  | ``
362 | 0 | `synchronized(System.in) {`
363 | 0 | `if (!(System.in instanceof ThreadLocalInputStream)) {`
364 | 0 | `System.setIn(new ThreadLocalInputStream(in));`
365 | 0 | `System.setOut(new ThreadLocalPrintStream(out));`
366 | 0 | `System.setErr(new ThreadLocalPrintStream(err));`
367 |  | `}`
368 | 0 | `}`
369 |  | ``
370 |  | `try {`
371 | 0 | `if (addr == null) {`
372 | 0 | `serversocket = new ServerSocket(port);`
373 |  | `} else {`
374 | 0 | `serversocket = new ServerSocket(port, 0, addr);`
375 |  | `}`
376 |  | ``
377 | 0 | `while (!shutdown) {`
378 | 0 | `sessionOnDeck = sessionPool.take();`
379 | 0 | `Socket socket = serversocket.accept();`
380 | 0 | `sessionOnDeck.run(socket);`
381 |  | `}`
382 |  | ``
383 | 0 | `} catch (Throwable t) {`
384 |  | `// if shutdown is called while the accept() method is blocking,`
385 |  | `// an exception will be thrown that we don't care about. filter`
386 |  | `// those out.`
387 | 0 | `if (!shutdown) {`
388 | 0 | `t.printStackTrace();`
389 |  | `}`
390 | 0 | `}`
391 | 0 | `if (sessionOnDeck != null) {`
392 | 0 | `sessionOnDeck.shutdown();`
393 |  | `}`
394 | 0 | `running = false;`
395 | 0 | `}`
396 |  | ``
397 |  | `private static void usage() {`
398 | 0 | `System.err.println("Usage: java com.martiansoftware.nailgun.NGServer");`
399 | 0 | `System.err.println(" or: java com.martiansoftware.nailgun.NGServer port");`
400 | 0 | `System.err.println(" or: java com.martiansoftware.nailgun.NGServer IPAddress");`
401 | 0 | `System.err.println(" or: java com.martiansoftware.nailgun.NGServer IPAddress:port");`
402 | 0 | `}`
403 |  | ``
404 |  | `/**`
405 |  | `* Creates and starts a new <code>NGServer</code>. A single optional`
406 |  | `* argument is valid, specifying the port on which this <code>NGServer</code>`
407 |  | `* should listen. If omitted, <code>NGServer.DEFAULT_PORT</code> will be used.`
408 |  | `* @param args a single optional argument specifying the port on which to listen.`
409 |  | `* @throws NumberFormatException if a non-numeric port is specified`
410 |  | `*/`
411 |  | `public static void main(String[] args) throws NumberFormatException, UnknownHostException {`
412 |  | ``
413 | 0 | `if (args.length > 1) {`
414 | 0 | `usage();`
415 | 0 | `return;`
416 |  | `}`
417 |  | ``
418 |  | `// null server address means bind to everything local`
419 | 0 | `InetAddress serverAddress = null;`
420 | 0 | `int port = NGConstants.DEFAULT_PORT;`
421 |  | ``
422 |  | `// parse the sole command line parameter, which`
423 |  | `// may be an inetaddress to bind to, a port number,`
424 |  | `// or an inetaddress followed by a port, separated`
425 |  | `// by a colon`
426 | 0 | `if (args.length != 0) {`
427 | 0 | `String[] argParts = args[0].split(":");`
428 | 0 | `String addrPart = null;`
429 | 0 | `String portPart = null;`
430 | 0 | `if (argParts.length == 2) {`
431 | 0 | `addrPart = argParts[0];`
432 | 0 | `portPart = argParts[1];`
433 | 0 | `} else if (argParts[0].indexOf('.') >= 0) {`
434 | 0 | `addrPart = argParts[0];`
435 |  | `} else {`
436 | 0 | `portPart = argParts[0];`
437 |  | `}`
438 | 0 | `if (addrPart != null) {`
439 | 0 | `serverAddress = InetAddress.getByName(addrPart);`
440 |  | `}`
441 | 0 | `if (portPart != null) {`
442 | 0 | `port = Integer.parseInt(portPart);`
443 |  | `}`
444 |  | `}`
445 |  | ``
446 | 0 | `NGServer server = new NGServer(serverAddress, port);`
447 | 0 | `Thread t = new Thread(server);`
448 | 0 | `t.setName("NGServer(" + serverAddress + ", " + port + ")");`
449 | 0 | `t.start();`
450 |  | ``
451 | 0 | `Runtime.getRuntime().addShutdownHook(new NGServerShutdowner(server));`
452 |  | ``
453 |  | `// if the port is 0, it will be automatically determined.`
454 |  | `// add this little wait so the ServerSocket can fully`
455 |  | `// initialize and we can see what port it chose.`
456 | 0 | `int runningPort = server.getPort();`
457 | 0 | `while (runningPort == 0) {`
458 | 0 | `try { Thread.sleep(50); } catch (Throwable toIgnore) {}`
459 | 0 | `runningPort = server.getPort();`
460 |  | `}`
461 |  | ``
462 | 0 | `System.out.println("NGServer started on "`
463 |  | `+ ((serverAddress == null)`
464 |  | `? "all interfaces"`
465 |  | `: serverAddress.getHostAddress())`
466 |  | `+ ", port "`
467 |  | `+ runningPort`
468 |  | `+ ".");`
469 | 0 | `}`
470 |  | ``
471 |  | `/**`
472 |  | `* A shutdown hook that will cleanly bring down the NGServer if it`
473 |  | `* is interrupted.`
474 |  | `*`
475 |  | `* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>`
476 |  | `*/`
477 |  | `private static class NGServerShutdowner extends Thread {`
478 |  | `private NGServer server = null;`
479 |  | ``
480 |  | `NGServerShutdowner(NGServer server) {`
481 |  | `this.server = server;`
482 |  | `}`
483 |  | ``
484 |  | ``
485 |  | `public void run() {`
486 |  | ``
487 |  | `int count = 0;`
488 |  | `server.shutdown(false);`
489 |  | ``
490 |  | `// give the server up to five seconds to stop. is that enough?`
491 |  | `// remember that the shutdown will call nailShutdown in any`
492 |  | `// nails as well`
493 |  | `while (server.isRunning() && (count < 50)) {`
494 |  | ``
495 |  | `try {Thread.sleep(100);} catch(InterruptedException e) {}`
496 |  | `++count;`
497 |  | `}`
498 |  | ``
499 |  | `if (server.isRunning()) {`
500 |  | `System.err.println("Unable to cleanly shutdown server. Exiting JVM Anyway.");`
501 |  | `} else {`
502 |  | `System.out.println("NGServer shut down.");`
503 |  | `}`
504 |  | `}`
505 |  | `}`
506 |  | `}`

this report was generated by version 1.0.5 of jcoverage. visit www.jcoverage.com for updates. | copyright © 2003, jcoverage ltd. all rights reserved. Java is a trademark of Sun Microsystems, Inc. in the United States and other countries.