# Coverage details for com.martiansoftware.nailgun.NGSession

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 |  | ``
21 |  | `import java.io.InputStream;`
22 |  | `import java.io.PrintStream;`
23 |  | `import java.lang.reflect.InvocationTargetException;`
24 |  | `import java.lang.reflect.Method;`
25 |  | `import java.net.Socket;`
26 |  | `import java.util.List;`
27 |  | `import java.util.Properties;`
28 |  | ``
29 |  | `import org.apache.tools.ant.ExitException;`
30 |  | ``
31 |  | `/**`
32 |  | `* Reads the NailGun stream from the client through the command,`
33 |  | `* then hands off processing to the appropriate class. The NGSession`
34 |  | `* obtains its sockets from an NGSessionPool, which created this`
35 |  | `* NGSession.`
36 |  | `*`
37 |  | `* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>`
38 |  | `*/`
39 |  | `class NGSession extends Thread {`
40 |  | ``
41 |  | `/**`
42 |  | `* The server this NGSession is working for`
43 |  | `*/`
44 | 0 | `private NGServer server = null;`
45 |  | ``
46 |  | `/**`
47 |  | `* The pool this NGSession came from, and to which it will`
48 |  | `* return itself`
49 |  | `*/`
50 | 0 | `private NGSessionPool sessionPool = null;`
51 |  | ``
52 |  | `/**`
53 |  | `* Synchronization object`
54 |  | `*/`
55 | 0 | `private Object lock = new Object();`
56 |  | ``
57 |  | `/**`
58 |  | `* The next socket this NGSession has been tasked with processing`
59 |  | `* (by NGServer)`
60 |  | `*/`
61 | 0 | `private Socket nextSocket = null;`
62 |  | ``
63 |  | `/**`
64 |  | `* True if the server has been shutdown and this NGSession should`
65 |  | `* terminate completely`
66 |  | `*/`
67 | 0 | `private boolean done = false;`
68 |  | ``
69 |  | `/**`
70 |  | `* The instance number of this NGSession. That is, if this is the Nth`
71 |  | `* NGSession to be created, then this is the value for N.`
72 |  | `*/`
73 | 0 | `private long instanceNumber = 0;`
74 |  | ``
75 |  | `/**`
76 |  | `* A lock shared among all NGSessions`
77 |  | `*/`
78 | 0 | `private static Object sharedLock = new Object();`
79 |  | ``
80 |  | `/**`
81 |  | `* The instance counter shared among all NGSessions`
82 |  | `*/`
83 | 0 | `private static long instanceCounter = 0;`
84 |  | ``
85 |  | `/**`
86 |  | `* signature of main(String[]) for reflection operations`
87 |  | `*/`
88 |  | `private static Class[] mainSignature;`
89 |  | ``
90 |  | `/**`
91 |  | `* signature of nailMain(NGContext) for reflection operations`
92 |  | `*/`
93 |  | `private static Class[] nailMainSignature;`
94 |  | ``
95 |  | `static {`
96 |  | `// initialize the signatures`
97 | 0 | `mainSignature = new Class[1];`
98 | 0 | `mainSignature[0] = String[].class;`
99 |  | ``
100 | 0 | `nailMainSignature = new Class[1];`
101 | 0 | `nailMainSignature[0] = NGContext.class;`
102 | 0 | `}`
103 |  | ``
104 |  | `/**`
105 |  | `* Creates a new NGSession running for the specified NGSessionPool and`
106 |  | `* NGServer.`
107 |  | `* @param sessionPool The NGSessionPool we're working for`
108 |  | `* @param server The NGServer we're working for`
109 |  | `*/`
110 |  | `NGSession(NGSessionPool sessionPool, NGServer server) {`
111 | 0 | `super();`
112 | 0 | `this.sessionPool = sessionPool;`
113 | 0 | `this.server = server;`
114 |  | ``
115 | 0 | `synchronized(sharedLock) {`
116 | 0 | `this.instanceNumber = ++instanceCounter;`
117 | 0 | `}`
118 |  | `// server.out.println("Created NGSession " + instanceNumber);`
119 | 0 | `}`
120 |  | ``
121 |  | `/**`
122 |  | `* Shuts down this NGSession gracefully`
123 |  | `*/`
124 |  | `void shutdown() {`
125 | 0 | `done = true;`
126 | 0 | `synchronized(lock) {`
127 | 0 | `nextSocket = null;`
128 | 0 | `lock.notifyAll();`
129 | 0 | `}`
130 | 0 | `}`
131 |  | ``
132 |  | `/**`
133 |  | `* Instructs this NGSession to process the specified socket, after which`
134 |  | `* this NGSession will return itself to the pool from which it came.`
135 |  | `* @param socket the socket (connected to a client) to process`
136 |  | `*/`
137 |  | `public void run(Socket socket) {`
138 | 0 | `synchronized(lock) {`
139 | 0 | `nextSocket = socket;`
140 | 0 | `lock.notify();`
141 | 0 | `}`
142 | 0 | `Thread.yield();`
143 | 0 | `}`
144 |  | ``
145 |  | `/**`
146 |  | `* Returns the next socket to process. This will block the NGSession`
147 |  | `* thread until there's a socket to process or the NGSession has been`
148 |  | `* shut down.`
149 |  | `*`
150 |  | `* @return the next socket to process, or <code>null</code> if the NGSession`
151 |  | `* has been shut down.`
152 |  | `*/`
153 |  | `private Socket nextSocket() {`
154 | 0 | `Socket result = null;`
155 | 0 | `synchronized(lock) {`
156 | 0 | `result = nextSocket;`
157 | 0 | `while (!done && result == null) {`
158 |  | `try {`
159 | 0 | `lock.wait();`
160 | 0 | `} catch (InterruptedException e) {`
161 | 0 | `done = true;`
162 | 0 | `}`
163 | 0 | `result = nextSocket;`
164 |  | `}`
165 | 0 | `nextSocket = null;`
166 | 0 | `}`
167 | 0 | `return (result);`
168 |  | `}`
169 |  | ``
170 |  | `/**`
171 |  | `* The main NGSession loop. This gets the next socket to process, runs`
172 |  | `* the nail for the socket, and loops until shut down.`
173 |  | `*/`
174 |  | `public void run() {`
175 |  | ``
176 | 0 | `updateThreadName(null);`
177 |  | ``
178 | 0 | `Socket socket = nextSocket();`
179 | 0 | `while (socket != null) {`
180 |  | `try {`
181 |  | `// buffer for reading headers`
182 | 0 | `byte[] lbuf = new byte[5];`
183 | 0 | `java.io.DataInputStream sockin = new java.io.DataInputStream(socket.getInputStream());`
184 | 0 | `java.io.OutputStream sockout = socket.getOutputStream();`
185 |  | ``
186 |  | `// client info - command line arguments and environment`
187 | 0 | `List remoteArgs = new java.util.ArrayList();`
188 | 0 | `Properties remoteEnv = new Properties();`
189 |  | ``
190 | 0 | `String cwd = null; // working directory`
191 | 0 | `String command = null; // alias or class name`
192 |  | ``
193 |  | `// read everything from the client up to and including the command`
194 | 0 | `while (command == null) {`
195 | 0 | `sockin.readFully(lbuf);`
196 | 0 | `long bytesToRead = LongUtils.fromArray(lbuf, 0);`
197 | 0 | `char chunkType = (char) lbuf[4];`
198 |  | ``
199 | 0 | `byte[] b = new byte[(int) bytesToRead];`
200 | 0 | `sockin.readFully(b);`
201 | 0 | `String line = new String(b, "US-ASCII");`
202 |  | ``
203 | 0 | `switch(chunkType) {`
204 |  | ``
205 |  | `case NGConstants.CHUNKTYPE_ARGUMENT:`
206 |  | `// command line argument`
207 | 0 | `remoteArgs.add(line);`
208 | 0 | `break;`
209 |  | ``
210 |  | `case NGConstants.CHUNKTYPE_ENVIRONMENT:`
211 |  | `// parse environment into property`
212 | 0 | `int equalsIndex = line.indexOf('=');`
213 | 0 | `if (equalsIndex > 0) {`
214 | 0 | `remoteEnv.setProperty(`
215 |  | `line.substring(0, equalsIndex),`
216 |  | `line.substring(equalsIndex + 1));`
217 |  | `}`
218 | 0 | `String key = line.substring(0, equalsIndex);`
219 | 0 | `break;`
220 |  | ``
221 |  | `case NGConstants.CHUNKTYPE_COMMAND:`
222 |  | `// command (alias or classname)`
223 | 0 | `command = line;`
224 | 0 | `break;`
225 |  | ``
226 |  | `case NGConstants.CHUNKTYPE_WORKINGDIRECTORY:`
227 |  | `// client working directory`
228 | 0 | `cwd = line;`
229 | 0 | `break;`
230 |  | ``
231 |  | `default: // freakout?`
232 |  | `}`
233 |  | `}`
234 |  | ``
235 | 0 | `updateThreadName(socket.getInetAddress().getHostAddress() + ": " + command);`
236 |  | ``
237 |  | `// can't create NGInputStream until we've received a command, because at`
238 |  | `// that point the stream from the client will only include stdin and stdin-eof`
239 |  | `// chunks`
240 | 0 | `InputStream in = new NGInputStream(sockin);`
241 | 0 | `PrintStream out = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDOUT));`
242 | 0 | `PrintStream err = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDERR));`
243 | 0 | `PrintStream exit = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_EXIT));`
244 |  | ``
245 |  | `// ThreadLocal streams for System.in/out/err redirection`
246 | 0 | `((ThreadLocalInputStream) System.in).init(in);`
247 | 0 | `((ThreadLocalPrintStream) System.out).init(out);`
248 | 0 | `((ThreadLocalPrintStream) System.err).init(err);`
249 |  | ``
250 |  | `try {`
251 | 0 | `Alias alias = server.getAliasManager().getAlias(command);`
252 | 0 | `Class cmdclass = null;`
253 | 0 | `if (alias != null) {`
254 | 0 | `cmdclass = alias.getAliasedClass();`
255 | 0 | `} else if (server.allowsNailsByClassName()) {`
256 | 0 | `cmdclass = Class.forName(command);`
257 |  | `} else {`
258 | 0 | `cmdclass = server.getDefaultNailClass();`
259 |  | `}`
260 |  | ``
261 | 0 | `Object[] methodArgs = new Object[1];`
262 | 0 | `Method mainMethod = null; // will be either main(String[]) or nailMain(NGContext)`
263 | 0 | `String[] cmdlineArgs = (String[]) remoteArgs.toArray(new String[remoteArgs.size()]);`
264 |  | ``
265 |  | `try {`
266 | 0 | `mainMethod = cmdclass.getMethod("nailMain", nailMainSignature);`
267 | 0 | `NGContext context = new NGContext();`
268 | 0 | `context.setArgs(cmdlineArgs);`
269 | 0 | `context.in = in;`
270 | 0 | `context.out = out;`
271 | 0 | `context.err = err;`
272 | 0 | `context.setCommand(command);`
273 | 0 | `context.setExitStream(exit);`
274 | 0 | `context.setNGServer(server);`
275 | 0 | `context.setEnv(remoteEnv);`
276 | 0 | `context.setInetAddress(socket.getInetAddress());`
277 | 0 | `context.setPort(socket.getPort());`
278 | 0 | `context.setWorkingDirectory(cwd);`
279 | 0 | `methodArgs[0] = context;`
280 | 0 | `} catch (NoSuchMethodException toDiscard) {`
281 |  | `// that's ok - we'll just try main(String[]) next.`
282 | 0 | `}`
283 |  | ``
284 | 0 | `if (mainMethod == null) {`
285 | 0 | `mainMethod = cmdclass.getMethod("main", mainSignature);`
286 | 0 | `methodArgs[0] = cmdlineArgs;`
287 |  | `}`
288 |  | ``
289 | 0 | `if (mainMethod != null) {`
290 | 0 | `server.nailStarted(cmdclass);`
291 | 0 | `NGSecurityManager.setExit(exit);`
292 |  | ``
293 |  | `try {`
294 | 0 | `mainMethod.invoke(null, methodArgs);`
295 | 0 | `} catch (InvocationTargetException ite) {`
296 | 0 | `throw(ite.getCause());`
297 | 0 | `} catch (Throwable t) {`
298 | 0 | `throw(t);`
299 |  | `} finally {`
300 | 0 | `server.nailFinished(cmdclass);`
301 | 0 | `}`
302 | 0 | `exit.println(0);`
303 |  | `}`
304 |  | ``
305 | 0 | `} catch (ExitException exitEx) {`
306 | 0 | `exit.println(exitEx.getStatus());`
307 | 0 | `server.out.println(Thread.currentThread().getName() + " exited with status " + exitEx.getStatus());`
308 | 0 | `} catch (Throwable t) {`
309 | 0 | `t.printStackTrace();`
310 | 0 | `exit.println(NGConstants.EXIT_EXCEPTION); // remote exception constant`
311 | 0 | `}`
312 |  | ``
313 | 0 | `socket.close();`
314 |  | ``
315 | 0 | `} catch (Throwable t) {`
316 | 0 | `t.printStackTrace();`
317 | 0 | `}`
318 |  | ``
319 | 0 | `((ThreadLocalInputStream) System.in).init(null);`
320 | 0 | `((ThreadLocalPrintStream) System.out).init(null);`
321 | 0 | `((ThreadLocalPrintStream) System.err).init(null);`
322 |  | ``
323 | 0 | `updateThreadName(null);`
324 | 0 | `sessionPool.give(this);`
325 | 0 | `socket = nextSocket();`
326 |  | `}`
327 |  | ``
328 |  | `// server.out.println("Shutdown NGSession " + instanceNumber);`
329 | 0 | `}`
330 |  | ``
331 |  | `/**`
332 |  | `* Updates the current thread name (useful for debugging).`
333 |  | `*/`
334 |  | `private void updateThreadName(String detail) {`
335 | 0 | `setName("NGSession " + instanceNumber + ": " + ((detail == null) ? "(idle)" : detail));`
336 | 0 | `}`
337 |  | `}`

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.