KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > winstone > Launcher


1 /*
2  * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
3  * Distributed under the terms of either:
4  * - the common development and distribution license (CDDL), v1.0; or
5  * - the GNU Lesser General Public License, v2.1 or later
6  */

7 package winstone;
8
9 import java.io.File JavaDoc;
10 import java.io.FileInputStream JavaDoc;
11 import java.io.FileOutputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.InputStream JavaDoc;
14 import java.io.InterruptedIOException JavaDoc;
15 import java.io.ObjectInputStream JavaDoc;
16 import java.io.OutputStream JavaDoc;
17 import java.lang.reflect.Constructor JavaDoc;
18 import java.net.ServerSocket JavaDoc;
19 import java.net.Socket JavaDoc;
20 import java.net.URL JavaDoc;
21 import java.net.URLClassLoader JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Properties JavaDoc;
28
29 /**
30  * Implements the main launcher daemon thread. This is the class that gets
31  * launched by the command line, and owns the server socket, etc.
32  *
33  * @author <a HREF="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
34  * @version $Id: Launcher.java,v 1.28 2006/08/12 07:33:11 rickknowles Exp $
35  */

36 public class Launcher implements Runnable JavaDoc {
37     
38     static final String JavaDoc HTTP_LISTENER_CLASS = "winstone.HttpListener";
39     static final String JavaDoc HTTPS_LISTENER_CLASS = "winstone.ssl.HttpsListener";
40     static final String JavaDoc AJP_LISTENER_CLASS = "winstone.ajp13.Ajp13Listener";
41     static final String JavaDoc CLUSTER_CLASS = "winstone.cluster.SimpleCluster";
42     static final String JavaDoc DEFAULT_JNDI_MGR_CLASS = "winstone.jndi.ContainerJNDIManager";
43
44     public static final byte SHUTDOWN_TYPE = (byte) '0';
45     public static final byte RELOAD_TYPE = (byte) '4';
46     
47     private int CONTROL_TIMEOUT = 2000; // wait 2s for control connection
48
private int DEFAULT_CONTROL_PORT = -1;
49     
50     private Thread JavaDoc controlThread;
51     public final static WinstoneResourceBundle RESOURCES = new WinstoneResourceBundle("winstone.LocalStrings");
52     private int controlPort;
53     private HostGroup hostGroup;
54     private ObjectPool objectPool;
55     private List JavaDoc listeners;
56     private Map JavaDoc args;
57     private Cluster cluster;
58     private JNDIManager globalJndiManager;
59     
60     /**
61      * Constructor - initialises the web app, object pools, control port and the
62      * available protocol listeners.
63      */

64     public Launcher(Map JavaDoc args) throws IOException JavaDoc {
65         
66         boolean useJNDI = WebAppConfiguration.booleanArg(args, "useJNDI", false);
67         
68         // Set jndi resource handler if not set (workaround for JamVM bug)
69
if (useJNDI) try {
70             Class JavaDoc ctxFactoryClass = Class.forName("winstone.jndi.java.javaURLContextFactory");
71             if (System.getProperty("java.naming.factory.initial") == null) {
72                 System.setProperty("java.naming.factory.initial", ctxFactoryClass.getName());
73             }
74             if (System.getProperty("java.naming.factory.url.pkgs") == null) {
75                 System.setProperty("java.naming.factory.url.pkgs", "winstone.jndi");
76             }
77         } catch (ClassNotFoundException JavaDoc err) {}
78         
79         Logger.log(Logger.MAX, RESOURCES, "Launcher.StartupArgs", args + "");
80         
81         this.args = args;
82         this.controlPort = (args.get("controlPort") == null ? DEFAULT_CONTROL_PORT
83                 : Integer.parseInt((String JavaDoc) args.get("controlPort")));
84
85         // Check for java home
86
List JavaDoc jars = new ArrayList JavaDoc();
87         List JavaDoc commonLibCLPaths = new ArrayList JavaDoc();
88         String JavaDoc defaultJavaHome = System.getProperty("java.home");
89         String JavaDoc javaHome = WebAppConfiguration.stringArg(args, "javaHome", defaultJavaHome);
90         Logger.log(Logger.DEBUG, RESOURCES, "Launcher.UsingJavaHome", javaHome);
91         String JavaDoc toolsJarLocation = WebAppConfiguration.stringArg(args, "toolsJar", null);
92         File JavaDoc toolsJar = null;
93         if (toolsJarLocation == null) {
94             toolsJar = new File JavaDoc(javaHome, "lib/tools.jar");
95
96             // first try - if it doesn't exist, try up one dir since we might have
97
// the JRE home by mistake
98
if (!toolsJar.exists()) {
99                 File JavaDoc javaHome2 = new File JavaDoc(javaHome).getParentFile();
100                 File JavaDoc toolsJar2 = new File JavaDoc(javaHome2, "lib/tools.jar");
101                 if (toolsJar2.exists()) {
102                     javaHome = javaHome2.getCanonicalPath();
103                     toolsJar = toolsJar2;
104                 }
105             }
106         } else {
107             toolsJar = new File JavaDoc(toolsJarLocation);
108         }
109
110         // Add tools jar to classloader path
111
if (toolsJar.exists()) {
112             jars.add(toolsJar.toURL());
113             commonLibCLPaths.add(toolsJar);
114             Logger.log(Logger.DEBUG, RESOURCES, "Launcher.AddedCommonLibJar",
115                     toolsJar.getName());
116         } else if (WebAppConfiguration.booleanArg(args, "useJasper", false))
117             Logger.log(Logger.WARNING, RESOURCES, "Launcher.ToolsJarNotFound");
118
119         // Set up common lib class loader
120
String JavaDoc commonLibCLFolder = WebAppConfiguration.stringArg(args,
121                 "commonLibFolder", "lib");
122         File JavaDoc libFolder = new File JavaDoc(commonLibCLFolder);
123         if (libFolder.exists() && libFolder.isDirectory()) {
124             Logger.log(Logger.DEBUG, RESOURCES, "Launcher.UsingCommonLib",
125                     libFolder.getCanonicalPath());
126             File JavaDoc children[] = libFolder.listFiles();
127             for (int n = 0; n < children.length; n++)
128                 if (children[n].getName().endsWith(".jar")
129                         || children[n].getName().endsWith(".zip")) {
130                     jars.add(children[n].toURL());
131                     commonLibCLPaths.add(children[n]);
132                     Logger.log(Logger.DEBUG, RESOURCES, "Launcher.AddedCommonLibJar",
133                             children[n].getName());
134                 }
135         } else {
136             Logger.log(Logger.DEBUG, RESOURCES, "Launcher.NoCommonLib");
137         }
138         ClassLoader JavaDoc commonLibCL = new URLClassLoader JavaDoc((URL JavaDoc[]) jars.toArray(new URL JavaDoc[0]),
139                 getClass().getClassLoader());
140         
141         Logger.log(Logger.MAX, RESOURCES, "Launcher.CLClassLoader",
142                 commonLibCL.toString());
143                                         
144         this.objectPool = new ObjectPool(args);
145
146         // Optionally set up clustering if enabled and libraries are available
147
String JavaDoc useCluster = (String JavaDoc) args.get("useCluster");
148         boolean switchOnCluster = (useCluster != null)
149                 && (useCluster.equalsIgnoreCase("true") || useCluster
150                         .equalsIgnoreCase("yes"));
151         if (switchOnCluster) {
152             if (this.controlPort < 0) {
153                 Logger.log(Logger.INFO, RESOURCES,
154                         "Launcher.ClusterOffNoControlPort");
155             } else {
156                 String JavaDoc clusterClassName = WebAppConfiguration.stringArg(args, "clusterClassName",
157                         CLUSTER_CLASS).trim();
158                 try {
159                     Class JavaDoc clusterClass = Class.forName(clusterClassName);
160                     Constructor JavaDoc clusterConstructor = clusterClass
161                             .getConstructor(new Class JavaDoc[] { Map JavaDoc.class, Integer JavaDoc.class });
162                     this.cluster = (Cluster) clusterConstructor
163                             .newInstance(new Object JavaDoc[] { args, new Integer JavaDoc(this.controlPort) });
164                 } catch (ClassNotFoundException JavaDoc err) {
165                     Logger.log(Logger.DEBUG, RESOURCES, "Launcher.ClusterNotFound");
166                 } catch (Throwable JavaDoc err) {
167                     Logger.log(Logger.WARNING, RESOURCES, "Launcher.ClusterStartupError", err);
168                 }
169             }
170         }
171         
172         // If jndi is enabled, run the container wide jndi populator
173
if (useJNDI) {
174             String JavaDoc jndiMgrClassName = WebAppConfiguration.stringArg(args, "containerJndiClassName",
175                     DEFAULT_JNDI_MGR_CLASS).trim();
176             try {
177                 // Build the realm
178
Class JavaDoc jndiMgrClass = Class.forName(jndiMgrClassName, true, commonLibCL);
179                 Constructor JavaDoc jndiMgrConstr = jndiMgrClass.getConstructor(new Class JavaDoc[] {
180                         Map JavaDoc.class, List JavaDoc.class, ClassLoader JavaDoc.class });
181                 this.globalJndiManager = (JNDIManager) jndiMgrConstr.newInstance(new Object JavaDoc[] {
182                         args, null, commonLibCL });
183                 this.globalJndiManager.setup();
184             } catch (ClassNotFoundException JavaDoc err) {
185                 Logger.log(Logger.DEBUG, RESOURCES,
186                         "Launcher.JNDIDisabled");
187             } catch (Throwable JavaDoc err) {
188                 Logger.log(Logger.ERROR, RESOURCES,
189                         "Launcher.JNDIError", jndiMgrClassName, err);
190             }
191         }
192         
193         // Open the web apps
194
this.hostGroup = new HostGroup(this.cluster, this.objectPool, commonLibCL,
195                 (File JavaDoc []) commonLibCLPaths.toArray(new File JavaDoc[0]), args);
196
197         // Create connectors (http, https and ajp)
198
this.listeners = new ArrayList JavaDoc();
199         spawnListener(HTTP_LISTENER_CLASS);
200         spawnListener(AJP_LISTENER_CLASS);
201         try {
202             Class.forName("javax.net.ServerSocketFactory");
203             spawnListener(HTTPS_LISTENER_CLASS);
204         } catch (ClassNotFoundException JavaDoc err) {
205             Logger.log(Logger.DEBUG, RESOURCES,
206                     "Launcher.NeedsJDK14", HTTPS_LISTENER_CLASS);
207         }
208
209         this.controlThread = new Thread JavaDoc(this, RESOURCES.getString(
210                 "Launcher.ThreadName", "" + this.controlPort));
211         this.controlThread.setDaemon(false);
212         this.controlThread.start();
213
214         Runtime.getRuntime().addShutdownHook(new ShutdownHook(this));
215
216     }
217
218     /**
219      * Instantiates listeners. Note that an exception thrown in the
220      * constructor is interpreted as the listener being disabled, so
221      * don't do anything too adventurous in the constructor, or if you do,
222      * catch and log any errors locally before rethrowing.
223      */

224     protected void spawnListener(String JavaDoc listenerClassName) {
225         try {
226             Class JavaDoc listenerClass = Class.forName(listenerClassName);
227             Constructor JavaDoc listenerConstructor = listenerClass
228                     .getConstructor(new Class JavaDoc[] { Map JavaDoc.class,
229                             ObjectPool.class, HostGroup.class});
230             Listener listener = (Listener) listenerConstructor
231                     .newInstance(new Object JavaDoc[] { args, this.objectPool,
232                             this.hostGroup });
233             if (listener.start()) {
234                 this.listeners.add(listener);
235             }
236         } catch (ClassNotFoundException JavaDoc err) {
237             Logger.log(Logger.INFO, RESOURCES,
238                     "Launcher.ListenerNotFound", listenerClassName);
239         } catch (Throwable JavaDoc err) {
240             Logger.log(Logger.ERROR, RESOURCES,
241                     "Launcher.ListenerStartupError", listenerClassName, err);
242         }
243     }
244
245     /**
246      * The main run method. This handles the normal thread processing.
247      */

248     public void run() {
249         boolean interrupted = false;
250         try {
251             ServerSocket JavaDoc controlSocket = null;
252
253             if (this.controlPort > 0) {
254                 controlSocket = new ServerSocket JavaDoc(this.controlPort);
255                 controlSocket.setSoTimeout(CONTROL_TIMEOUT);
256             }
257
258             Logger.log(Logger.INFO, RESOURCES, "Launcher.StartupOK",
259                     new String JavaDoc[] {RESOURCES.getString("ServerVersion"),
260                                     (this.controlPort > 0 ? "" + this.controlPort
261                                             : RESOURCES.getString("Launcher.ControlDisabled"))});
262
263             // Enter the main loop
264
while (!interrupted) {
265 // this.objectPool.removeUnusedRequestHandlers();
266
// this.hostGroup.invalidateExpiredSessions();
267

268                 // Check for control request
269
Socket JavaDoc accepted = null;
270                 try {
271                     if (controlSocket != null) {
272                         accepted = controlSocket.accept();
273                         if (accepted != null) {
274                             handleControlRequest(accepted);
275                         }
276                     } else {
277                         Thread.sleep(CONTROL_TIMEOUT);
278                     }
279                 } catch (InterruptedIOException JavaDoc err) {
280                 } catch (InterruptedException JavaDoc err) {
281                     interrupted = true;
282                 } catch (Throwable JavaDoc err) {
283                     Logger.log(Logger.ERROR, RESOURCES,
284                             "Launcher.ShutdownError", err);
285                 } finally {
286                     if (accepted != null) {
287                         try {accepted.close();} catch (IOException JavaDoc err) {}
288                     }
289                     if (Thread.interrupted()) {
290                         interrupted = true;
291                     }
292                 }
293             }
294
295             // Close server socket
296
if (controlSocket != null) {
297                 controlSocket.close();
298             }
299         } catch (Throwable JavaDoc err) {
300             Logger.log(Logger.ERROR, RESOURCES, "Launcher.ShutdownError", err);
301         }
302         Logger.log(Logger.INFO, RESOURCES, "Launcher.ControlThreadShutdownOK");
303     }
304
305     protected void handleControlRequest(Socket JavaDoc csAccepted) throws IOException JavaDoc {
306         InputStream JavaDoc inSocket = null;
307         OutputStream JavaDoc outSocket = null;
308         ObjectInputStream JavaDoc inControl = null;
309         try {
310             inSocket = csAccepted.getInputStream();
311             int reqType = inSocket.read();
312             if ((byte) reqType == SHUTDOWN_TYPE) {
313                 Logger.log(Logger.INFO, RESOURCES,
314                         "Launcher.ShutdownRequestReceived");
315                 shutdown();
316             } else if ((byte) reqType == RELOAD_TYPE) {
317                 inControl = new ObjectInputStream JavaDoc(inSocket);
318                 String JavaDoc host = inControl.readUTF();
319                 String JavaDoc prefix = inControl.readUTF();
320                 Logger.log(Logger.INFO, RESOURCES, "Launcher.ReloadRequestReceived", host + prefix);
321                 HostConfiguration hostConfig = this.hostGroup.getHostByName(host);
322                 hostConfig.reloadWebApp(prefix);
323             } else if (this.cluster != null) {
324                 outSocket = csAccepted.getOutputStream();
325                 this.cluster.clusterRequest((byte) reqType,
326                         inSocket, outSocket, csAccepted,
327                         this.hostGroup);
328             }
329         } finally {
330             if (inControl != null) {
331                 try {inControl.close();} catch (IOException JavaDoc err) {}
332             }
333             if (inSocket != null) {
334                 try {inSocket.close();} catch (IOException JavaDoc err) {}
335             }
336             if (outSocket != null) {
337                 try {outSocket.close();} catch (IOException JavaDoc err) {}
338             }
339         }
340     }
341     
342     public void shutdown() {
343         // Release all listeners/pools/webapps
344
for (Iterator JavaDoc i = this.listeners.iterator(); i.hasNext();)
345             ((Listener) i.next()).destroy();
346         this.objectPool.destroy();
347         if (this.cluster != null)
348             this.cluster.destroy();
349         this.hostGroup.destroy();
350         if (this.globalJndiManager != null) {
351             this.globalJndiManager.tearDown();
352         }
353
354         if (this.controlThread != null) {
355             this.controlThread.interrupt();
356         }
357         Thread.yield();
358
359         Logger.log(Logger.INFO, RESOURCES, "Launcher.ShutdownOK");
360     }
361
362     public boolean isRunning() {
363         return (this.controlThread != null) && this.controlThread.isAlive();
364     }
365     
366     /**
367      * Main method. This basically just accepts a few args, then initialises the
368      * listener thread. For now, just shut it down with a control-C.
369      */

370     public static void main(String JavaDoc argv[]) throws IOException JavaDoc {
371         Map JavaDoc args = getArgsFromCommandLine(argv);
372         
373         if (args.containsKey("usage") || args.containsKey("help")) {
374             printUsage();
375             return;
376         }
377
378         // Check for embedded war
379
deployEmbeddedWarfile(args);
380         
381         // Check for embedded warfile
382
if (!args.containsKey("webroot") && !args.containsKey("warfile")
383                 && !args.containsKey("webappsDir")&& !args.containsKey("hostsDir")) {
384             printUsage();
385             return;
386         }
387         // Launch
388
try {
389             new Launcher(args);
390         } catch (Throwable JavaDoc err) {
391             Logger.log(Logger.ERROR, RESOURCES, "Launcher.ContainerStartupError", err);
392         }
393     }
394     
395     public static Map JavaDoc getArgsFromCommandLine(String JavaDoc argv[]) throws IOException JavaDoc {
396         Map JavaDoc args = loadArgsFromCommandLineAndConfig(argv, "nonSwitch");
397         
398         // Small hack to allow re-use of the command line parsing inside the control tool
399
String JavaDoc firstNonSwitchArgument = (String JavaDoc) args.get("nonSwitch");
400         args.remove("nonSwitch");
401         
402         // Check if the non-switch arg is a file or folder, and overwrite the config
403
if (firstNonSwitchArgument != null) {
404             File JavaDoc webapp = new File JavaDoc(firstNonSwitchArgument);
405             if (webapp.exists()) {
406                 if (webapp.isDirectory()) {
407                     args.put("webroot", firstNonSwitchArgument);
408                 } else if (webapp.isFile()) {
409                     args.put("warfile", firstNonSwitchArgument);
410                 }
411             }
412         }
413         return args;
414     }
415
416     public static Map JavaDoc loadArgsFromCommandLineAndConfig(String JavaDoc argv[], String JavaDoc nonSwitchArgName)
417             throws IOException JavaDoc {
418         Map JavaDoc args = new HashMap JavaDoc();
419         
420         // Load embedded properties file
421
String JavaDoc embeddedPropertiesFilename = RESOURCES.getString(
422                 "Launcher.EmbeddedPropertiesFile");
423         
424         InputStream JavaDoc embeddedPropsStream = Launcher.class.getResourceAsStream(
425                 embeddedPropertiesFilename);
426         if (embeddedPropsStream != null) {
427             loadPropsFromStream(embeddedPropsStream, args);
428             embeddedPropsStream.close();
429         }
430         
431         // Get command line args
432
String JavaDoc configFilename = RESOURCES.getString("Launcher.DefaultPropertyFile");
433         for (int n = 0; n < argv.length; n++) {
434             String JavaDoc option = argv[n];
435             if (option.startsWith("--")) {
436                 int equalPos = option.indexOf('=');
437                 String JavaDoc paramName = option.substring(2,
438                         equalPos == -1 ? option.length() : equalPos);
439                 if (equalPos != -1) {
440                     args.put(paramName, option.substring(equalPos + 1));
441                 } else {
442                     args.put(paramName, "true");
443                 }
444                 if (paramName.equals("config")) {
445                     configFilename = (String JavaDoc) args.get(paramName);
446                 }
447             } else {
448                 args.put(nonSwitchArgName, option);
449             }
450         }
451
452         // Load default props if available
453
File JavaDoc configFile = new File JavaDoc(configFilename);
454         if (configFile.exists() && configFile.isFile()) {
455             InputStream JavaDoc inConfig = new FileInputStream JavaDoc(configFile);
456             loadPropsFromStream(inConfig, args);
457             inConfig.close();
458             initLogger(args);
459             Logger.log(Logger.DEBUG, RESOURCES, "Launcher.UsingPropertyFile",
460                     configFilename);
461         } else {
462             initLogger(args);
463         }
464         return args;
465     }
466     
467     protected static void deployEmbeddedWarfile(Map JavaDoc args) throws IOException JavaDoc {
468         String JavaDoc embeddedWarfileName = RESOURCES.getString("Launcher.EmbeddedWarFile");
469         InputStream JavaDoc embeddedWarfile = Launcher.class.getResourceAsStream(
470                 embeddedWarfileName);
471         if (embeddedWarfile != null) {
472             File JavaDoc tempWarfile = File.createTempFile("embedded", ".war").getAbsoluteFile();
473             tempWarfile.getParentFile().mkdirs();
474             tempWarfile.deleteOnExit();
475
476             String JavaDoc embeddedWebroot = RESOURCES.getString("Launcher.EmbeddedWebroot");
477             File JavaDoc tempWebroot = new File JavaDoc(tempWarfile.getParentFile(), embeddedWebroot);
478             tempWebroot.mkdirs();
479             
480             Logger.log(Logger.DEBUG, RESOURCES, "Launcher.CopyingEmbeddedWarfile",
481                     tempWarfile.getAbsolutePath());
482             OutputStream JavaDoc out = new FileOutputStream JavaDoc(tempWarfile, true);
483             int read = 0;
484             byte buffer[] = new byte[2048];
485             while ((read = embeddedWarfile.read(buffer)) != -1) {
486                 out.write(buffer, 0, read);
487             }
488             out.close();
489             embeddedWarfile.close();
490             
491             args.put("warfile", tempWarfile.getAbsolutePath());
492             args.put("webroot", tempWebroot.getAbsolutePath());
493             args.remove("webappsDir");
494             args.remove("hostsDir");
495         }
496     }
497     
498     protected static void loadPropsFromStream(InputStream JavaDoc inConfig, Map JavaDoc args) throws IOException JavaDoc {
499         Properties JavaDoc props = new Properties JavaDoc();
500         props.load(inConfig);
501         for (Iterator JavaDoc i = props.keySet().iterator(); i.hasNext(); ) {
502             String JavaDoc key = (String JavaDoc) i.next();
503             if (!args.containsKey(key.trim())) {
504                 args.put(key.trim(), props.getProperty(key).trim());
505             }
506         }
507         props.clear();
508     }
509     
510     public static void initLogger(Map JavaDoc args) throws IOException JavaDoc {
511         // Reset the log level
512
int logLevel = WebAppConfiguration.intArg(args, "debug", Logger.INFO);
513 // boolean showThrowingLineNo = WebAppConfiguration.booleanArg(args, "logThrowingLineNo", false);
514
boolean showThrowingThread = WebAppConfiguration.booleanArg(args, "logThrowingThread", false);
515         OutputStream JavaDoc logStream = null;
516         if (args.get("logfile") != null) {
517             logStream = new FileOutputStream JavaDoc((String JavaDoc) args.get("logfile"));
518         } else if (WebAppConfiguration.booleanArg(args, "logToStdErr", false)) {
519             logStream = System.err;
520         } else {
521             logStream = System.out;
522         }
523 // Logger.init(logLevel, logStream, showThrowingLineNo, showThrowingThread);
524
Logger.init(logLevel, logStream, showThrowingThread);
525     }
526
527     protected static void printUsage() {
528         System.out.println(RESOURCES.getString("Launcher.UsageInstructions",
529                 RESOURCES.getString("ServerVersion")));
530     }
531 }
532
Popular Tags