001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jk.server;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FileOutputStream;
022: import java.io.IOException;
023: import java.io.PrintStream;
024: import java.util.Enumeration;
025: import java.util.Hashtable;
026: import java.util.Properties;
027: import java.util.StringTokenizer;
028: import java.util.Vector;
029:
030: import javax.management.MBeanRegistration;
031: import javax.management.MBeanServer;
032: import javax.management.ObjectName;
033:
034: import org.apache.commons.modeler.Registry;
035: import org.apache.jk.core.JkHandler;
036: import org.apache.jk.core.WorkerEnv;
037: import org.apache.tomcat.util.IntrospectionUtils;
038:
039: /** Main class used to startup and configure jk. It manages the conf/jk2.properties file
040: * and is the target of JMX proxy.
041: *
042: * It implements a policy of save-on-change - whenever a property is changed at
043: * runtime the jk2.properties file will be overriden.
044: *
045: * You can edit the config file when tomcat is stoped ( or if you don't use JMX or
046: * other admin tools ).
047: *
048: * The format of jk2.properties:
049: * <dl>
050: * <dt>TYPE[.LOCALNAME].PROPERTY_NAME=VALUE
051: * <dd>Set a property on the associated component. TYPE will be used to
052: * find the class name and instantiate the component. LOCALNAME allows
053: * multiple instances. In JMX mode, TYPE and LOCALNAME will form the
054: * JMX name ( eventually combined with a 'jk2' component )
055: *
056: * <dt>NAME=VALUE
057: * <dd>Define global properties to be used in ${} substitutions
058: *
059: * <dt>class.COMPONENT_TYPE=JAVA_CLASS_NAME
060: * <dd>Adds a new 'type' of component. We predefine all known types.
061: * </dl>
062: *
063: * Instances are created the first time a component name is found. In addition,
064: * 'handler.list' property will override the list of 'default' components that are
065: * loaded automatically.
066: *
067: * Note that the properties file is just one (simplistic) way to configure jk. We hope
068: * to see configs based on registry, LDAP, db, etc. ( XML is not necesarily better )
069: *
070: * @author Costin Manolache
071: * @deprecated Will be replaced with JMX operations
072: */
073: public class JkMain implements MBeanRegistration {
074: WorkerEnv wEnv;
075: String propFile;
076: Properties props = new Properties();
077:
078: Properties modules = new Properties();
079: boolean modified = false;
080: boolean started = false;
081: boolean saveProperties = false;
082:
083: public JkMain() {
084: JkMain.jkMain = this ;
085: modules.put("channelSocket",
086: "org.apache.jk.common.ChannelSocket");
087: modules.put("channelUnix", "org.apache.jk.common.ChannelUn");
088: modules.put("channelJni", "org.apache.jk.common.ChannelJni");
089: modules.put("apr", "org.apache.jk.apr.AprImpl");
090: modules.put("mx", "org.apache.jk.common.JkMX");
091: modules.put("modeler", "org.apache.jk.common.JkModeler");
092: modules.put("shm", "org.apache.jk.common.Shm");
093: modules.put("request", "org.apache.jk.common.HandlerRequest");
094: modules.put("container", "org.apache.jk.common.HandlerRequest");
095: modules.put("modjk", "org.apache.jk.common.ModJkMX");
096:
097: }
098:
099: public static JkMain getJkMain() {
100: return jkMain;
101: }
102:
103: private static String DEFAULT_HTTPS = "com.sun.net.ssl.internal.www.protocol";
104:
105: private void initHTTPSUrls() {
106: try {
107: // 11657: if only ajp is used, https: redirects need to work ( at least for 1.3+)
108: String value = System
109: .getProperty("java.protocol.handler.pkgs");
110: if (value == null) {
111: value = DEFAULT_HTTPS;
112: } else if (value.indexOf(DEFAULT_HTTPS) >= 0) {
113: return; // already set
114: } else {
115: value += "|" + DEFAULT_HTTPS;
116: }
117: System.setProperty("java.protocol.handler.pkgs", value);
118: } catch (Exception ex) {
119: ex.printStackTrace();
120: }
121: }
122:
123: // -------------------- Setting --------------------
124:
125: /** Load a .properties file into and set the values
126: * into jk2 configuration.
127: */
128: public void setPropertiesFile(String p) {
129: propFile = p;
130: try {
131: props.load(new FileInputStream(propFile));
132: } catch (IOException ex) {
133: ex.printStackTrace();
134: }
135: }
136:
137: public String getPropertiesFile() {
138: return propFile;
139: }
140:
141: public void setSaveProperties(boolean b) {
142: saveProperties = b;
143: }
144:
145: /** Set a name/value as a jk2 property
146: */
147: public void setProperty(String n, String v) {
148: if ("jkHome".equals(n)) {
149: setJkHome(v);
150: }
151: props.put(n, v);
152: if (started) {
153: processProperty(n, v);
154: saveProperties();
155: }
156: }
157:
158: /**
159: * Retrieve a property.
160: */
161: public Object getProperty(String name) {
162: String alias = (String) replacements.get(name);
163: Object result = null;
164: if (alias != null) {
165: result = props.get(alias);
166: }
167: if (result == null) {
168: result = props.get(name);
169: }
170: return result;
171: }
172:
173: /**
174: * Set the <code>channelClassName</code> that will used to connect to
175: * httpd.
176: */
177: public void setChannelClassName(String name) {
178: props.put("handler.channel.className", name);
179: }
180:
181: public String getChannelClassName() {
182: return (String) props.get("handler.channel.className");
183: }
184:
185: /**
186: * Set the <code>workerClassName</code> that will handle the request.
187: * ( sort of 'pivot' in axis :-)
188: */
189: public void setWorkerClassName(String name) {
190: props.put("handler.container.className", name);
191: }
192:
193: public String getWorkerClassName() {
194: return (String) props.get("handler.container.className");
195: }
196:
197: /** Set the base dir of jk2. ( including WEB-INF if in a webapp ).
198: * We'll try to guess it from classpath if none is set ( for
199: * example on command line ), but if in a servlet environment
200: * you need to use Context.getRealPath or a system property or
201: * set it expliciltey.
202: */
203: public void setJkHome(String s) {
204: getWorkerEnv().setJkHome(s);
205: }
206:
207: public String getJkHome() {
208: return getWorkerEnv().getJkHome();
209: }
210:
211: String out;
212: String err;
213: File propsF;
214:
215: public void setOut(String s) {
216: this .out = s;
217: }
218:
219: public String getOut() {
220: return this .out;
221: }
222:
223: public void setErr(String s) {
224: this .err = s;
225: }
226:
227: public String getErr() {
228: return this .err;
229: }
230:
231: // -------------------- Initialization --------------------
232:
233: public void init() throws IOException {
234: long t1 = System.currentTimeMillis();
235: if (null != out) {
236: PrintStream outS = new PrintStream(
237: new FileOutputStream(out));
238: System.setOut(outS);
239: }
240: if (null != err) {
241: PrintStream errS = new PrintStream(
242: new FileOutputStream(err));
243: System.setErr(errS);
244: }
245:
246: String home = getWorkerEnv().getJkHome();
247: if (home == null) {
248: // XXX use IntrospectionUtil to find myself
249: this .guessHome();
250: }
251: home = getWorkerEnv().getJkHome();
252: if (home == null) {
253: log.info("Can't find home, jk2.properties not loaded");
254: }
255: if (home != null) {
256: File hF = new File(home);
257: File conf = new File(home, "conf");
258: if (!conf.exists())
259: conf = new File(home, "etc");
260:
261: propsF = new File(conf, "jk2.properties");
262:
263: if (propsF.exists()) {
264: log.debug("Starting Jk2, base dir= " + home + " conf="
265: + propsF);
266: setPropertiesFile(propsF.getAbsolutePath());
267: } else {
268: log.debug("Starting Jk2, base dir= " + home);
269: if (log.isDebugEnabled())
270: log.debug("No properties file found " + propsF);
271: }
272: }
273: String initHTTPS = (String) props.get("class.initHTTPS");
274: if ("true".equalsIgnoreCase(initHTTPS)) {
275: initHTTPSUrls();
276: }
277:
278: long t2 = System.currentTimeMillis();
279: initTime = t2 - t1;
280: }
281:
282: static String defaultHandlers[] = { "request", "container",
283: "channelSocket" };
284:
285: /*
286: static String defaultHandlers[]= { "apr",
287: "shm",
288: "request",
289: "container",
290: "channelSocket",
291: "channelJni",
292: "channelUnix"};
293: */
294:
295: public void stop() {
296: for (int i = 0; i < wEnv.getHandlerCount(); i++) {
297: if (wEnv.getHandler(i) != null) {
298: try {
299: wEnv.getHandler(i).destroy();
300: } catch (IOException ex) {
301: log.error("Error stoping "
302: + wEnv.getHandler(i).getName(), ex);
303: }
304: }
305: }
306:
307: started = false;
308: }
309:
310: public void start() throws IOException {
311: long t1 = System.currentTimeMillis();
312: // We must have at least 3 handlers:
313: // channel is the 'transport'
314: // request is the request processor or 'global' chain
315: // container is the 'provider'
316: // Additional handlers may exist and be used internally
317: // or be chained to create one of the standard handlers
318:
319: String handlers[] = defaultHandlers;
320: // backward compat
321: String workers = props.getProperty("handler.list", null);
322: if (workers != null) {
323: handlers = split(workers, ",");
324: }
325:
326: // Load additional component declarations
327: processModules();
328:
329: for (int i = 0; i < handlers.length; i++) {
330: String name = handlers[i];
331: JkHandler w = getWorkerEnv().getHandler(name);
332: if (w == null) {
333: newHandler(name, "", name);
334: }
335: }
336:
337: // Process properties - and add aditional handlers.
338: processProperties();
339:
340: for (int i = 0; i < wEnv.getHandlerCount(); i++) {
341: if (wEnv.getHandler(i) != null) {
342: try {
343: wEnv.getHandler(i).init();
344: } catch (IOException ex) {
345: if ("apr".equals(wEnv.getHandler(i).getName())) {
346: log
347: .info("APR not loaded, disabling jni components: "
348: + ex.toString());
349: } else {
350: log.error("error initializing "
351: + wEnv.getHandler(i).getName(), ex);
352: }
353: }
354: }
355: }
356:
357: started = true;
358: long t2 = System.currentTimeMillis();
359: startTime = t2 - t1;
360:
361: this .saveProperties();
362: log.info("Jk running ID=" + wEnv.getLocalId() + " time="
363: + initTime + "/" + startTime + " config=" + propFile);
364: }
365:
366: // -------------------- Usefull methods --------------------
367:
368: public WorkerEnv getWorkerEnv() {
369: if (wEnv == null) {
370: wEnv = new WorkerEnv();
371: }
372: return wEnv;
373: }
374:
375: public void setWorkerEnv(WorkerEnv wEnv) {
376: this .wEnv = wEnv;
377: }
378:
379: /* A bit of magic to support workers.properties without giving
380: up the clean get/set
381: */
382: public void setBeanProperty(Object target, String name, String val) {
383: if (val != null)
384: val = IntrospectionUtils.replaceProperties(val, props);
385: if (log.isDebugEnabled())
386: log.debug("setProperty " + target + " " + name + "=" + val);
387:
388: IntrospectionUtils.setProperty(target, name, val);
389: }
390:
391: /*
392: * Set a handler property
393: */
394: public void setPropertyString(String handlerN, String name,
395: String val) {
396: if (log.isDebugEnabled())
397: log.debug("setProperty " + handlerN + " " + name + "="
398: + val);
399: Object target = getWorkerEnv().getHandler(handlerN);
400:
401: setBeanProperty(target, name, val);
402: if (started) {
403: saveProperties();
404: }
405:
406: }
407:
408: /** The time it took to initialize jk ( ms)
409: */
410: public long getInitTime() {
411: return initTime;
412: }
413:
414: /** The time it took to start jk ( ms )
415: */
416: public long getStartTime() {
417: return startTime;
418: }
419:
420: // -------------------- Main --------------------
421:
422: long initTime;
423: long startTime;
424: static JkMain jkMain = null;
425:
426: public static void main(String args[]) {
427: try {
428: if (args.length == 1
429: && ("-?".equals(args[0]) || "-h".equals(args[0]))) {
430: System.out.println("Usage: ");
431: System.out.println(" JkMain [args]");
432: System.out.println();
433: System.out
434: .println(" Each bean setter corresponds to an arg ( like -debug 10 )");
435: System.out.println(" System properties:");
436: System.out.println(" jk2.home Base dir of jk2");
437: return;
438: }
439:
440: jkMain = new JkMain();
441:
442: IntrospectionUtils.processArgs(jkMain, args,
443: new String[] {}, null, new Hashtable());
444:
445: jkMain.init();
446: jkMain.start();
447: } catch (Exception ex) {
448: ex.printStackTrace();
449: }
450: }
451:
452: // -------------------- Private methods --------------------
453:
454: public void saveProperties() {
455: if (!saveProperties)
456: return;
457:
458: // Temp - to check if it works
459: String outFile = propFile + ".save";
460: log.debug("Saving properties " + outFile);
461: try {
462: props.save(new FileOutputStream(outFile),
463: "AUTOMATICALLY GENERATED");
464: } catch (IOException ex) {
465: ex.printStackTrace();
466: }
467: }
468:
469: // translate top-level keys ( from coyote or generic ) into component keys
470: static Hashtable replacements = new Hashtable();
471: static {
472: replacements.put("port", "channelSocket.port");
473: replacements.put("maxThreads", "channelSocket.maxThreads");
474: replacements.put("minSpareThreads",
475: "channelSocket.minSpareThreads");
476: replacements.put("maxSpareThreads",
477: "channelSocket.maxSpareThreads");
478: replacements.put("backlog", "channelSocket.backlog");
479: replacements.put("tcpNoDelay", "channelSocket.tcpNoDelay");
480: replacements.put("soTimeout", "channelSocket.soTimeout");
481: replacements.put("timeout", "channelSocket.timeout");
482: replacements.put("address", "channelSocket.address");
483: replacements.put("tomcatAuthentication",
484: "request.tomcatAuthentication");
485: }
486:
487: private void preProcessProperties() {
488: Enumeration keys = props.keys();
489: Vector v = new Vector();
490:
491: while (keys.hasMoreElements()) {
492: String key = (String) keys.nextElement();
493: Object newName = replacements.get(key);
494: if (newName != null) {
495: v.addElement(key);
496: }
497: }
498: keys = v.elements();
499: while (keys.hasMoreElements()) {
500: String key = (String) keys.nextElement();
501: Object propValue = props.getProperty(key);
502: String replacement = (String) replacements.get(key);
503: props.put(replacement, propValue);
504: if (log.isDebugEnabled())
505: log.debug("Substituting " + key + " " + replacement
506: + " " + propValue);
507: }
508: }
509:
510: private void processProperties() {
511: preProcessProperties();
512: Enumeration keys = props.keys();
513:
514: while (keys.hasMoreElements()) {
515: String name = (String) keys.nextElement();
516: String propValue = props.getProperty(name);
517:
518: processProperty(name, propValue);
519: }
520: }
521:
522: private void processProperty(String name, String propValue) {
523: String type = name;
524: String fullName = name;
525: String localName = "";
526: String propName = "";
527: // ignore
528: if (name.startsWith("key."))
529: return;
530:
531: int dot = name.indexOf(".");
532: int lastDot = name.lastIndexOf(".");
533: if (dot > 0) {
534: type = name.substring(0, dot);
535: if (dot != lastDot) {
536: localName = name.substring(dot + 1, lastDot);
537: fullName = type + "." + localName;
538: } else {
539: fullName = type;
540: }
541: propName = name.substring(lastDot + 1);
542: } else {
543: return;
544: }
545:
546: if (log.isDebugEnabled())
547: log.debug("Processing " + type + ":" + localName + ":"
548: + fullName + " " + propName);
549: if ("class".equals(type) || "handler".equals(type)) {
550: return;
551: }
552:
553: JkHandler comp = getWorkerEnv().getHandler(fullName);
554: if (comp == null) {
555: comp = newHandler(type, localName, fullName);
556: }
557: if (comp == null)
558: return;
559:
560: if (log.isDebugEnabled())
561: log.debug("Setting " + propName + " on " + fullName + " "
562: + comp);
563: this .setBeanProperty(comp, propName, propValue);
564: }
565:
566: private JkHandler newHandler(String type, String localName,
567: String fullName) {
568: JkHandler handler;
569: String classN = modules.getProperty(type);
570: if (classN == null) {
571: log.error("No class name for " + fullName + " " + type);
572: return null;
573: }
574: try {
575: Class channelclass = Class.forName(classN);
576: handler = (JkHandler) channelclass.newInstance();
577: } catch (Throwable ex) {
578: handler = null;
579: log.error("Can't create " + fullName, ex);
580: return null;
581: }
582: if (this .domain != null) {
583: try {
584: Registry.getRegistry().registerComponent(handler,
585: this .domain, classN,
586: "type=JkHandler,name=" + fullName);
587: } catch (Exception e) {
588: log.error("Error registering " + fullName, e);
589: }
590:
591: }
592: wEnv.addHandler(fullName, handler);
593: return handler;
594: }
595:
596: private void processModules() {
597: Enumeration keys = props.keys();
598: int plen = 6;
599:
600: while (keys.hasMoreElements()) {
601: String k = (String) keys.nextElement();
602: if (!k.startsWith("class."))
603: continue;
604:
605: String name = k.substring(plen);
606: String propValue = props.getProperty(k);
607:
608: if (log.isDebugEnabled())
609: log.debug("Register " + name + " " + propValue);
610: modules.put(name, propValue);
611: }
612: }
613:
614: private String[] split(String s, String delim) {
615: Vector v = new Vector();
616: StringTokenizer st = new StringTokenizer(s, delim);
617: while (st.hasMoreTokens()) {
618: v.addElement(st.nextToken());
619: }
620: String res[] = new String[v.size()];
621: for (int i = 0; i < res.length; i++) {
622: res[i] = (String) v.elementAt(i);
623: }
624: return res;
625: }
626:
627: // guessing home
628: private static String CNAME = "org/apache/jk/server/JkMain.class";
629:
630: private void guessHome() {
631: String home = wEnv.getJkHome();
632: if (home != null)
633: return;
634: home = IntrospectionUtils.guessInstall("jk2.home", "jk2.home",
635: "tomcat-jk2.jar", CNAME);
636: if (home != null) {
637: log.info("Guessed home " + home);
638: wEnv.setJkHome(home);
639: }
640: }
641:
642: static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
643: .getLog(JkMain.class);
644:
645: protected String domain;
646: protected ObjectName oname;
647: protected MBeanServer mserver;
648:
649: public ObjectName getObjectName() {
650: return oname;
651: }
652:
653: public String getDomain() {
654: return domain;
655: }
656:
657: public ObjectName preRegister(MBeanServer server, ObjectName name)
658: throws Exception {
659: oname = name;
660: mserver = server;
661: domain = name.getDomain();
662: return name;
663: }
664:
665: public void postRegister(Boolean registrationDone) {
666: }
667:
668: public void preDeregister() throws Exception {
669: }
670:
671: public void postDeregister() {
672: }
673:
674: public void pause() throws Exception {
675: for (int i = 0; i < wEnv.getHandlerCount(); i++) {
676: if (wEnv.getHandler(i) != null) {
677: wEnv.getHandler(i).pause();
678: }
679: }
680: }
681:
682: public void resume() throws Exception {
683: for (int i = 0; i < wEnv.getHandlerCount(); i++) {
684: if (wEnv.getHandler(i) != null) {
685: wEnv.getHandler(i).resume();
686: }
687: }
688: }
689:
690: }
|