KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > martiansoftware > nailgun > NGSession


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 JavaDoc;
22 import java.io.PrintStream JavaDoc;
23 import java.lang.reflect.InvocationTargetException JavaDoc;
24 import java.lang.reflect.Method JavaDoc;
25 import java.net.Socket JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Properties JavaDoc;
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 JavaDoc {
40
41     /**
42      * The server this NGSession is working for
43      */

44     private NGServer server = null;
45     
46     /**
47      * The pool this NGSession came from, and to which it will
48      * return itself
49      */

50     private NGSessionPool sessionPool = null;
51     
52     /**
53      * Synchronization object
54      */

55     private Object JavaDoc lock = new Object JavaDoc();
56
57     /**
58      * The next socket this NGSession has been tasked with processing
59      * (by NGServer)
60      */

61     private Socket JavaDoc nextSocket = null;
62     
63     /**
64      * True if the server has been shutdown and this NGSession should
65      * terminate completely
66      */

67     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     private long instanceNumber = 0;
74
75     /**
76      * A lock shared among all NGSessions
77      */

78     private static Object JavaDoc sharedLock = new Object JavaDoc();
79     
80     /**
81      * The instance counter shared among all NGSessions
82      */

83     private static long instanceCounter = 0;
84     
85     /**
86      * signature of main(String[]) for reflection operations
87      */

88     private static Class JavaDoc[] mainSignature;
89
90     /**
91      * signature of nailMain(NGContext) for reflection operations
92      */

93     private static Class JavaDoc[] nailMainSignature;
94     
95     static {
96         // initialize the signatures
97
mainSignature = new Class JavaDoc[1];
98         mainSignature[0] = String JavaDoc[].class;
99         
100         nailMainSignature = new Class JavaDoc[1];
101         nailMainSignature[0] = NGContext.class;
102     }
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         super();
112         this.sessionPool = sessionPool;
113         this.server = server;
114     
115         synchronized(sharedLock) {
116             this.instanceNumber = ++instanceCounter;
117         }
118 // server.out.println("Created NGSession " + instanceNumber);
119
}
120
121     /**
122      * Shuts down this NGSession gracefully
123      */

124     void shutdown() {
125         done = true;
126         synchronized(lock) {
127             nextSocket = null;
128             lock.notifyAll();
129         }
130     }
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 JavaDoc socket) {
138         synchronized(lock) {
139             nextSocket = socket;
140             lock.notify();
141         }
142         Thread.yield();
143     }
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 JavaDoc nextSocket() {
154         Socket JavaDoc result = null;
155         synchronized(lock) {
156             result = nextSocket;
157             while (!done && result == null) {
158                 try {
159                     lock.wait();
160                 } catch (InterruptedException JavaDoc e) {
161                     done = true;
162                 }
163                 result = nextSocket;
164             }
165             nextSocket = null;
166         }
167         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         updateThreadName(null);
177         
178         Socket JavaDoc socket = nextSocket();
179         while (socket != null) {
180             try {
181                 // buffer for reading headers
182
byte[] lbuf = new byte[5];
183                 java.io.DataInputStream JavaDoc sockin = new java.io.DataInputStream JavaDoc(socket.getInputStream());
184                 java.io.OutputStream JavaDoc sockout = socket.getOutputStream();
185     
186                 // client info - command line arguments and environment
187
List JavaDoc remoteArgs = new java.util.ArrayList JavaDoc();
188                 Properties JavaDoc remoteEnv = new Properties JavaDoc();
189                 
190                 String JavaDoc cwd = null; // working directory
191
String JavaDoc command = null; // alias or class name
192

193                 // read everything from the client up to and including the command
194
while (command == null) {
195                     sockin.readFully(lbuf);
196                     long bytesToRead = LongUtils.fromArray(lbuf, 0);
197                     char chunkType = (char) lbuf[4];
198                     
199                     byte[] b = new byte[(int) bytesToRead];
200                     sockin.readFully(b);
201                     String JavaDoc line = new String JavaDoc(b, "US-ASCII");
202     
203                     switch(chunkType) {
204                                     
205                         case NGConstants.CHUNKTYPE_ARGUMENT:
206                                     // command line argument
207
remoteArgs.add(line);
208                                     break;
209
210                         case NGConstants.CHUNKTYPE_ENVIRONMENT:
211                                     // parse environment into property
212
int equalsIndex = line.indexOf('=');
213                                     if (equalsIndex > 0) {
214                                         remoteEnv.setProperty(
215                                                 line.substring(0, equalsIndex),
216                                                 line.substring(equalsIndex + 1));
217                                     }
218                                     String JavaDoc key = line.substring(0, equalsIndex);
219                                     break;
220                                     
221                         case NGConstants.CHUNKTYPE_COMMAND:
222                                     // command (alias or classname)
223
command = line;
224                                     break;
225                                     
226                         case NGConstants.CHUNKTYPE_WORKINGDIRECTORY:
227                                     // client working directory
228
cwd = line;
229                                     break;
230                                     
231                         default: // freakout?
232
}
233                 }
234     
235                 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
InputStream JavaDoc in = new NGInputStream(sockin);
241                 PrintStream JavaDoc out = new PrintStream JavaDoc(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDOUT));
242                 PrintStream JavaDoc err = new PrintStream JavaDoc(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDERR));
243                 PrintStream JavaDoc exit = new PrintStream JavaDoc(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_EXIT));
244     
245                 // ThreadLocal streams for System.in/out/err redirection
246
((ThreadLocalInputStream) System.in).init(in);
247                 ((ThreadLocalPrintStream) System.out).init(out);
248                 ((ThreadLocalPrintStream) System.err).init(err);
249                 
250                 try {
251                     Alias alias = server.getAliasManager().getAlias(command);
252                     Class JavaDoc cmdclass = null;
253                     if (alias != null) {
254                         cmdclass = alias.getAliasedClass();
255                     } else if (server.allowsNailsByClassName()) {
256                         cmdclass = Class.forName(command);
257                     } else {
258                         cmdclass = server.getDefaultNailClass();
259                     }
260
261                     Object JavaDoc[] methodArgs = new Object JavaDoc[1];
262                     Method JavaDoc mainMethod = null; // will be either main(String[]) or nailMain(NGContext)
263
String JavaDoc[] cmdlineArgs = (String JavaDoc[]) remoteArgs.toArray(new String JavaDoc[remoteArgs.size()]);
264                     
265                     try {
266                         mainMethod = cmdclass.getMethod("nailMain", nailMainSignature);
267                         NGContext context = new NGContext();
268                         context.setArgs(cmdlineArgs);
269                         context.in = in;
270                         context.out = out;
271                         context.err = err;
272                         context.setCommand(command);
273                         context.setExitStream(exit);
274                         context.setNGServer(server);
275                         context.setEnv(remoteEnv);
276                         context.setInetAddress(socket.getInetAddress());
277                         context.setPort(socket.getPort());
278                         context.setWorkingDirectory(cwd);
279                         methodArgs[0] = context;
280                     } catch (NoSuchMethodException JavaDoc toDiscard) {
281                         // that's ok - we'll just try main(String[]) next.
282
}
283                     
284                     if (mainMethod == null) {
285                         mainMethod = cmdclass.getMethod("main", mainSignature);
286                         methodArgs[0] = cmdlineArgs;
287                     }
288                     
289                     if (mainMethod != null) {
290                         server.nailStarted(cmdclass);
291                         NGSecurityManager.setExit(exit);
292
293                         try {
294                             mainMethod.invoke(null, methodArgs);
295                         } catch (InvocationTargetException JavaDoc ite) {
296                             throw(ite.getCause());
297                         } catch (Throwable JavaDoc t) {
298                             throw(t);
299                         } finally {
300                             server.nailFinished(cmdclass);
301                         }
302                         exit.println(0);
303                     }
304
305                 } catch (ExitException exitEx) {
306                     exit.println(exitEx.getStatus());
307                     server.out.println(Thread.currentThread().getName() + " exited with status " + exitEx.getStatus());
308                 } catch (Throwable JavaDoc t) {
309                     t.printStackTrace();
310                     exit.println(NGConstants.EXIT_EXCEPTION); // remote exception constant
311
}
312                 
313                 socket.close();
314     
315             } catch (Throwable JavaDoc t) {
316                 t.printStackTrace();
317             }
318
319             ((ThreadLocalInputStream) System.in).init(null);
320             ((ThreadLocalPrintStream) System.out).init(null);
321             ((ThreadLocalPrintStream) System.err).init(null);
322             
323             updateThreadName(null);
324             sessionPool.give(this);
325             socket = nextSocket();
326         }
327
328 // server.out.println("Shutdown NGSession " + instanceNumber);
329
}
330     
331     /**
332      * Updates the current thread name (useful for debugging).
333      */

334     private void updateThreadName(String JavaDoc detail) {
335         setName("NGSession " + instanceNumber + ": " + ((detail == null) ? "(idle)" : detail));
336     }
337 }
338
Popular Tags