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