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.config;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.PrintWriter;
023:
024: import org.apache.catalina.Container;
025: import org.apache.catalina.Context;
026: import org.apache.catalina.Engine;
027: import org.apache.catalina.Host;
028: import org.apache.catalina.Lifecycle;
029: import org.apache.catalina.LifecycleEvent;
030: import org.apache.catalina.LifecycleListener;
031: import org.apache.catalina.Server;
032:
033: /**
034: Base class for automatic jk based configurations based on
035: the Tomcat server.xml settings and the war contexts
036: initialized during startup.
037: <p>
038: This config interceptor is enabled by inserting a Config
039: element in the <b><ContextManager></b> tag body inside
040: the server.xml file like so:
041: <pre>
042: * < ContextManager ... >
043: * ...
044: * <<b>???Config</b> <i>options</i> />
045: * ...
046: * < /ContextManager >
047: </pre>
048: where <i>options</i> can include any of the following attributes:
049: <ul>
050: <li><b>configHome</b> - default parent directory for the following paths.
051: If not set, this defaults to TOMCAT_HOME. Ignored
052: whenever any of the following paths is absolute.
053: </li>
054: <li><b>workersConfig</b> - path to workers.properties file used by
055: jk connector. If not set, defaults to
056: "conf/jk/workers.properties".</li>
057: <li><b>jkLog</b> - path to log file to be used by jk connector.</li>
058: <li><b>jkDebug</b> - Loglevel setting. May be debug, info, error, or emerg.
059: If not set, defaults to emerg.</li>
060: <li><b>jkWorker</b> The desired worker. Must be set to one of the workers
061: defined in the workers.properties file. "ajp12", "ajp13"
062: or "inprocess" are the workers found in the default
063: workers.properties file. If not specified, defaults
064: to "ajp13" if an Ajp13Interceptor is in use, otherwise
065: it defaults to "ajp12".</li>
066: <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
067: insure that all the behavior configured in the web.xml
068: file functions correctly. If false, let Apache serve
069: static resources. The default is true.
070: Warning: When false, some configuration in
071: the web.xml may not be duplicated in Apache.
072: Review the mod_jk conf file to see what
073: configuration is actually being set in Apache.</li>
074: <li><b>noRoot</b> - If true, the root context is not mapped to
075: Tomcat. If false and forwardAll is true, all requests
076: to the root context are mapped to Tomcat. If false and
077: forwardAll is false, only JSP and servlets requests to
078: the root context are mapped to Tomcat. When false,
079: to correctly serve Tomcat's root context you may also
080: need to modify the web server to point it's home
081: directory to Tomcat's root context directory.
082: Otherwise some content, such as the root index.html,
083: may be served by the web server before the connector
084: gets a chance to claim the request and pass it to Tomcat.
085: The default is true.</li>
086: </ul>
087: <p>
088: @author Costin Manolache
089: @author Larry Isaacs
090: @author Bill Barker
091: @version $Revision: 467222 $
092: */
093: public class BaseJkConfig implements LifecycleListener {
094: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
095: .getLog(BaseJkConfig.class);
096:
097: protected File configHome = null;
098: protected File workersConfig = null;
099:
100: protected File jkLog = null;
101: protected String jkDebug = "emerg";
102: protected String jkWorker = "ajp13";
103:
104: protected boolean noRoot = true;
105: protected boolean forwardAll = true;
106:
107: protected String tomcatHome;
108: protected boolean regenerate = false;
109: protected boolean append = false;
110: protected boolean legacy = true;
111:
112: // -------------------- Tomcat callbacks --------------------
113:
114: // Auto-config should be able to react to dynamic config changes,
115: // and regenerate the config.
116:
117: /**
118: * Generate the configuration - only when the server is
119: * completely initialized ( before starting )
120: */
121: public void lifecycleEvent(LifecycleEvent evt) {
122: if (Lifecycle.START_EVENT.equals(evt.getType())) {
123: execute(evt);
124: }
125: }
126:
127: /**
128: * Generate configuration files. Override with method to generate
129: * web server specific configuration.
130: */
131: public void execute(LifecycleEvent evt) {
132: initProperties();
133: PrintWriter mod_jk = null;
134: try {
135: mod_jk = getWriter();
136: } catch (IOException iex) {
137: log.warn("Unable to open config file");
138: return;
139: }
140: Lifecycle who = evt.getLifecycle();
141: if (who instanceof Server) {
142: executeServer((Server) who, mod_jk);
143: } else if (who instanceof Engine) {
144: executeEngine((Engine) who, mod_jk);
145: } else if (who instanceof Host) {
146: executeHost((Host) who, mod_jk);
147: } else if (who instanceof Context) {
148: executeContext((Context) who, mod_jk);
149: }
150: mod_jk.close();
151: }
152:
153: /**
154: * Generate configuration files. Override with method to generate
155: * web server specific configuration.
156: */
157: public void executeServer(Server svr, PrintWriter mod_jk) {
158: if (!append) {
159: if (!generateJkHead(mod_jk))
160: return;
161: generateSSLConfig(mod_jk);
162: generateJkTail(mod_jk);
163: }
164: }
165:
166: /**
167: * Generate SSL options
168: */
169: protected void generateSSLConfig(PrintWriter mod_jk) {
170: }
171:
172: /**
173: * Generate general options
174: */
175: protected boolean generateJkHead(PrintWriter mod_jk) {
176: return true;
177: }
178:
179: /**
180: * Generate general options
181: */
182: protected void generateJkTail(PrintWriter mod_jk) {
183: }
184:
185: /**
186: * Generate Virtual Host start
187: */
188: protected void generateVhostHead(Host host, PrintWriter mod_jk) {
189: }
190:
191: /**
192: * Generate Virtual Host end
193: */
194: protected void generateVhostTail(Host host, PrintWriter mod_jk) {
195: }
196:
197: /**
198: * Generate configuration files. Override with method to generate
199: * web server specific configuration.
200: */
201: protected void executeEngine(Engine egn, PrintWriter mod_jk) {
202: if (egn.getJvmRoute() != null) {
203: jkWorker = egn.getJvmRoute();
204: }
205: executeServer(egn.getService().getServer(), mod_jk);
206: Container[] children = egn.findChildren();
207: for (int ii = 0; ii < children.length; ii++) {
208: if (children[ii] instanceof Host) {
209: executeHost((Host) children[ii], mod_jk);
210: } else if (children[ii] instanceof Context) {
211: executeContext((Context) children[ii], mod_jk);
212: }
213: }
214: }
215:
216: /**
217: * Generate configuration files. Override with method to generate
218: * web server specific configuration.
219: */
220: protected void executeHost(Host hst, PrintWriter mod_jk) {
221: generateVhostHead(hst, mod_jk);
222: Container[] children = hst.findChildren();
223: for (int ii = 0; ii < children.length; ii++) {
224: if (children[ii] instanceof Context) {
225: executeContext((Context) children[ii], mod_jk);
226: }
227: }
228: generateVhostTail(hst, mod_jk);
229: }
230:
231: /**
232: * executes the ApacheConfig interceptor. This method generates apache
233: * configuration files for use with mod_jk.
234: * @param context a Context object.
235: * @param mod_jk Writer for output.
236: */
237: public void executeContext(Context context, PrintWriter mod_jk) {
238:
239: if (context.getPath().length() > 0 || !noRoot) {
240: String docRoot = context.getServletContext().getRealPath(
241: "/");
242: if (forwardAll || docRoot == null)
243: generateStupidMappings(context, mod_jk);
244: else
245: generateContextMappings(context, mod_jk);
246: }
247: }
248:
249: protected void generateStupidMappings(Context context,
250: PrintWriter mod_jk) {
251: }
252:
253: protected void generateContextMappings(Context context,
254: PrintWriter mod_jk) {
255: }
256:
257: /**
258: * Get the output Writer. Override with method to generate
259: * web server specific configuration.
260: */
261: protected PrintWriter getWriter() throws IOException {
262: return null;
263: }
264:
265: /**
266: * Get the host associated with this Container (if any).
267: */
268: protected Host getHost(Container child) {
269: while (child != null && !(child instanceof Host)) {
270: child = child.getParent();
271: }
272: return (Host) child;
273: }
274:
275: //-------------------- Properties --------------------
276:
277: /**
278: * Append to config file.
279: * Set to <code>true</code> if the config information should be
280: * appended.
281: */
282: public void setAppend(boolean apnd) {
283: append = apnd;
284: }
285:
286: /**
287: * If false, we'll try to generate a config that will
288: * let apache serve static files.
289: * The default is true, forward all requests in a context
290: * to tomcat.
291: */
292: public void setForwardAll(boolean b) {
293: forwardAll = b;
294: }
295:
296: /**
297: * Special option - do not generate mappings for the ROOT
298: * context. The default is true, and will not generate the mappings,
299: * not redirecting all pages to tomcat (since /* matches everything).
300: * This means that the web server's root remains intact but isn't
301: * completely servlet/JSP enabled. If the ROOT webapp can be configured
302: * with the web server serving static files, there's no problem setting
303: * this option to false. If not, then setting it true means the web
304: * server will be out of picture for all requests.
305: */
306: public void setNoRoot(boolean b) {
307: noRoot = b;
308: }
309:
310: /**
311: * set a path to the parent directory of the
312: * conf folder. That is, the parent directory
313: * within which path setters would be resolved against,
314: * if relative. For example if ConfigHome is set to "/home/tomcat"
315: * and regConfig is set to "conf/mod_jk.conf" then the resulting
316: * path used would be:
317: * "/home/tomcat/conf/mod_jk.conf".</p>
318: * <p>
319: * However, if the path is set to an absolute path,
320: * this attribute is ignored.
321: * <p>
322: * If not set, execute() will set this to TOMCAT_HOME.
323: * @param dir - path to a directory
324: */
325: public void setConfigHome(String dir) {
326: if (dir == null)
327: return;
328: File f = new File(dir);
329: if (!f.isDirectory()) {
330: throw new IllegalArgumentException(
331: "BaseConfig.setConfigHome(): "
332: + "Configuration Home must be a directory! : "
333: + dir);
334: }
335: configHome = f;
336: }
337:
338: /**
339: * set a path to the workers.properties file.
340: * @param path String path to workers.properties file
341: */
342: public void setWorkersConfig(String path) {
343: workersConfig = (path == null ? null : new File(path));
344: }
345:
346: /**
347: * set the path to the log file
348: * @param path String path to a file
349: */
350: public void setJkLog(String path) {
351: jkLog = (path == null ? null : new File(path));
352: }
353:
354: /**
355: * Set the verbosity level
356: * ( use debug, error, etc. ) If not set, no log is written.
357: */
358: public void setJkDebug(String level) {
359: jkDebug = level;
360: }
361:
362: /**
363: * Sets the JK worker.
364: * @param worker The worker
365: */
366: public void setJkWorker(String worker) {
367: jkWorker = worker;
368: }
369:
370: public void setLegacy(boolean legacy) {
371: this .legacy = legacy;
372: }
373:
374: // -------------------- Initialize/guess defaults --------------------
375:
376: /**
377: * Initialize defaults for properties that are not set
378: * explicitely
379: */
380: protected void initProperties() {
381: tomcatHome = System.getProperty("catalina.home");
382: File tomcatDir = new File(tomcatHome);
383: if (configHome == null) {
384: configHome = tomcatDir;
385: }
386: }
387:
388: // -------------------- Config Utils --------------------
389:
390: /**
391: * Add an extension mapping. Override with method to generate
392: * web server specific configuration
393: */
394: protected boolean addExtensionMapping(String ctxPath, String ext,
395: PrintWriter pw) {
396: return true;
397: }
398:
399: /**
400: * Add a fulling specified mapping. Override with method to generate
401: * web server specific configuration
402: */
403: protected boolean addMapping(String fullPath, PrintWriter pw) {
404: return true;
405: }
406:
407: // -------------------- General Utils --------------------
408:
409: protected String getAbsoluteDocBase(Context context) {
410: // Calculate the absolute path of the document base
411: String docBase = context.getServletContext().getRealPath("/");
412: docBase = docBase.substring(0, docBase.length() - 1);
413: if (!isAbsolute(docBase)) {
414: docBase = tomcatHome + "/" + docBase;
415: }
416: docBase = patch(docBase);
417: return docBase;
418: }
419:
420: // ------------------ Grabbed from FileUtil -----------------
421: public static File getConfigFile(File base, File configDir,
422: String defaultF) {
423: if (base == null)
424: base = new File(defaultF);
425: if (!base.isAbsolute()) {
426: if (configDir != null)
427: base = new File(configDir, base.getPath());
428: else
429: base = new File(base.getAbsolutePath()); //??
430: }
431: File parent = new File(base.getParent());
432: if (!parent.exists()) {
433: if (!parent.mkdirs()) {
434: throw new RuntimeException(
435: "Unable to create path to config file :"
436: + base.getAbsolutePath());
437: }
438: }
439: return base;
440: }
441:
442: public static String patch(String path) {
443: String patchPath = path;
444:
445: // Move drive spec to the front of the path
446: if (patchPath.length() >= 3 && patchPath.charAt(0) == '/'
447: && Character.isLetter(patchPath.charAt(1))
448: && patchPath.charAt(2) == ':') {
449: patchPath = patchPath.substring(1, 3) + "/"
450: + patchPath.substring(3);
451: }
452:
453: // Eliminate consecutive slashes after the drive spec
454: if (patchPath.length() >= 2
455: && Character.isLetter(patchPath.charAt(0))
456: && patchPath.charAt(1) == ':') {
457: char[] ca = patchPath.replace('/', '\\').toCharArray();
458: char c;
459: StringBuffer sb = new StringBuffer();
460:
461: for (int i = 0; i < ca.length; i++) {
462: if ((ca[i] != '\\')
463: || (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
464: if (i == 0 && Character.isLetter(ca[i])
465: && i < ca.length - 1 && ca[i + 1] == ':') {
466: c = Character.toUpperCase(ca[i]);
467: } else {
468: c = ca[i];
469: }
470:
471: sb.append(c);
472: }
473: }
474:
475: patchPath = sb.toString();
476: }
477:
478: // fix path on NetWare - all '/' become '\\' and remove duplicate '\\'
479: if (System.getProperty("os.name").startsWith("NetWare")
480: && path.length() >= 3 && path.indexOf(':') > 0) {
481: char[] ca = patchPath.replace('/', '\\').toCharArray();
482: StringBuffer sb = new StringBuffer();
483:
484: for (int i = 0; i < ca.length; i++) {
485: if ((ca[i] != '\\')
486: || (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
487: sb.append(ca[i]);
488: }
489: }
490: patchPath = sb.toString();
491: }
492:
493: return patchPath;
494: }
495:
496: public static boolean isAbsolute(String path) {
497: // normal file
498: if (path.startsWith("/"))
499: return true;
500:
501: if (path.startsWith(File.separator))
502: return true;
503:
504: // win c:
505: if (path.length() >= 3 && Character.isLetter(path.charAt(0))
506: && path.charAt(1) == ':')
507: return true;
508:
509: // NetWare volume:
510: if (System.getProperty("os.name").startsWith("NetWare")
511: && path.length() >= 3 && path.indexOf(':') > 0)
512: return true;
513:
514: return false;
515: }
516: }
|