Coverage details for com.martiansoftware.nailgun.NGSession

LineHitsSource
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      */
440    private NGServer server = null;
45     
46     /**
47      * The pool this NGSession came from, and to which it will
48      * return itself
49      */
500    private NGSessionPool sessionPool = null;
51     
52     /**
53      * Synchronization object
54      */
550    private Object lock = new Object();
56  
57     /**
58      * The next socket this NGSession has been tasked with processing
59      * (by NGServer)
60      */
610    private Socket nextSocket = null;
62     
63     /**
64      * True if the server has been shutdown and this NGSession should
65      * terminate completely
66      */
670    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      */
730    private long instanceNumber = 0;
74  
75     /**
76      * A lock shared among all NGSessions
77      */
780    private static Object sharedLock = new Object();
79     
80     /**
81      * The instance counter shared among all NGSessions
82      */
830    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
970        mainSignature = new Class[1];
980        mainSignature[0] = String[].class;
99         
1000        nailMainSignature = new Class[1];
1010        nailMainSignature[0] = NGContext.class;
1020    }
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) {
1110        super();
1120        this.sessionPool = sessionPool;
1130        this.server = server;
114     
1150        synchronized(sharedLock) {
1160            this.instanceNumber = ++instanceCounter;
1170        }
118 // server.out.println("Created NGSession " + instanceNumber);
1190    }
120  
121     /**
122      * Shuts down this NGSession gracefully
123      */
124     void shutdown() {
1250        done = true;
1260        synchronized(lock) {
1270            nextSocket = null;
1280            lock.notifyAll();
1290        }
1300    }
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) {
1380        synchronized(lock) {
1390            nextSocket = socket;
1400            lock.notify();
1410        }
1420        Thread.yield();
1430    }
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() {
1540        Socket result = null;
1550        synchronized(lock) {
1560            result = nextSocket;
1570            while (!done && result == null) {
158                 try {
1590                    lock.wait();
1600                } catch (InterruptedException e) {
1610                    done = true;
1620                }
1630                result = nextSocket;
164             }
1650            nextSocket = null;
1660        }
1670        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     
1760        updateThreadName(null);
177         
1780        Socket socket = nextSocket();
1790        while (socket != null) {
180             try {
181                 // buffer for reading headers
1820                byte[] lbuf = new byte[5];
1830                java.io.DataInputStream sockin = new java.io.DataInputStream(socket.getInputStream());
1840                java.io.OutputStream sockout = socket.getOutputStream();
185     
186                 // client info - command line arguments and environment
1870                List remoteArgs = new java.util.ArrayList();
1880                Properties remoteEnv = new Properties();
189                 
1900                String cwd = null; // working directory
1910                String command = null; // alias or class name
192                 
193                 // read everything from the client up to and including the command
1940                while (command == null) {
1950                    sockin.readFully(lbuf);
1960                    long bytesToRead = LongUtils.fromArray(lbuf, 0);
1970                    char chunkType = (char) lbuf[4];
198                     
1990                    byte[] b = new byte[(int) bytesToRead];
2000                    sockin.readFully(b);
2010                    String line = new String(b, "US-ASCII");
202     
2030                    switch(chunkType) {
204                                     
205                         case NGConstants.CHUNKTYPE_ARGUMENT:
206                                     // command line argument
2070                                    remoteArgs.add(line);
2080                                    break;
209  
210                         case NGConstants.CHUNKTYPE_ENVIRONMENT:
211                                     // parse environment into property
2120                                    int equalsIndex = line.indexOf('=');
2130                                    if (equalsIndex > 0) {
2140                                        remoteEnv.setProperty(
215                                                 line.substring(0, equalsIndex),
216                                                 line.substring(equalsIndex + 1));
217                                     }
2180                                    String key = line.substring(0, equalsIndex);
2190                                    break;
220                                     
221                         case NGConstants.CHUNKTYPE_COMMAND:
222                                     // command (alias or classname)
2230                                    command = line;
2240                                    break;
225                                     
226                         case NGConstants.CHUNKTYPE_WORKINGDIRECTORY:
227                                     // client working directory
2280                                    cwd = line;
2290                                    break;
230                                     
231                         default: // freakout?
232                     }
233                 }
234     
2350                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
2400                InputStream in = new NGInputStream(sockin);
2410                PrintStream out = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDOUT));
2420                PrintStream err = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDERR));
2430                PrintStream exit = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_EXIT));
244     
245                 // ThreadLocal streams for System.in/out/err redirection
2460                ((ThreadLocalInputStream) System.in).init(in);
2470                ((ThreadLocalPrintStream) System.out).init(out);
2480                ((ThreadLocalPrintStream) System.err).init(err);
249                 
250                 try {
2510                    Alias alias = server.getAliasManager().getAlias(command);
2520                    Class cmdclass = null;
2530                    if (alias != null) {
2540                        cmdclass = alias.getAliasedClass();
2550                    } else if (server.allowsNailsByClassName()) {
2560                        cmdclass = Class.forName(command);
257                     } else {
2580                        cmdclass = server.getDefaultNailClass();
259                     }
260  
2610                    Object[] methodArgs = new Object[1];
2620                    Method mainMethod = null; // will be either main(String[]) or nailMain(NGContext)
2630                    String[] cmdlineArgs = (String[]) remoteArgs.toArray(new String[remoteArgs.size()]);
264                     
265                     try {
2660                        mainMethod = cmdclass.getMethod("nailMain", nailMainSignature);
2670                        NGContext context = new NGContext();
2680                        context.setArgs(cmdlineArgs);
2690                        context.in = in;
2700                        context.out = out;
2710                        context.err = err;
2720                        context.setCommand(command);
2730                        context.setExitStream(exit);
2740                        context.setNGServer(server);
2750                        context.setEnv(remoteEnv);
2760                        context.setInetAddress(socket.getInetAddress());
2770                        context.setPort(socket.getPort());
2780                        context.setWorkingDirectory(cwd);
2790                        methodArgs[0] = context;
2800                    } catch (NoSuchMethodException toDiscard) {
281                         // that's ok - we'll just try main(String[]) next.
2820                    }
283                     
2840                    if (mainMethod == null) {
2850                        mainMethod = cmdclass.getMethod("main", mainSignature);
2860                        methodArgs[0] = cmdlineArgs;
287                     }
288                     
2890                    if (mainMethod != null) {
2900                        server.nailStarted(cmdclass);
2910                        NGSecurityManager.setExit(exit);
292  
293                         try {
2940                            mainMethod.invoke(null, methodArgs);
2950                        } catch (InvocationTargetException ite) {
2960                            throw(ite.getCause());
2970                        } catch (Throwable t) {
2980                            throw(t);
299                         } finally {
3000                            server.nailFinished(cmdclass);
3010                        }
3020                        exit.println(0);
303                     }
304  
3050                } catch (ExitException exitEx) {
3060                    exit.println(exitEx.getStatus());
3070                    server.out.println(Thread.currentThread().getName() + " exited with status " + exitEx.getStatus());
3080                } catch (Throwable t) {
3090                    t.printStackTrace();
3100                    exit.println(NGConstants.EXIT_EXCEPTION); // remote exception constant
3110                }
312                 
3130                socket.close();
314     
3150            } catch (Throwable t) {
3160                t.printStackTrace();
3170            }
318  
3190            ((ThreadLocalInputStream) System.in).init(null);
3200            ((ThreadLocalPrintStream) System.out).init(null);
3210            ((ThreadLocalPrintStream) System.err).init(null);
322             
3230            updateThreadName(null);
3240            sessionPool.give(this);
3250            socket = nextSocket();
326         }
327  
328 // server.out.println("Shutdown NGSession " + instanceNumber);
3290    }
330     
331     /**
332      * Updates the current thread name (useful for debugging).
333      */
334     private void updateThreadName(String detail) {
3350        setName("NGSession " + instanceNumber + ": " + ((detail == null) ? "(idle)" : detail));
3360    }
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.