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