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.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.2 $
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: protected boolean legacy = false;
108:
109: // -------------------- Tomcat callbacks --------------------
110:
111: // Auto-config should be able to react to dynamic config changes,
112: // and regenerate the config.
113:
114: /** Generate the configuration - only when the server is
115: * completely initialized ( before starting )
116: */
117: public void lifecycleEvent(LifecycleEvent evt) {
118: if (Lifecycle.START_EVENT.equals(evt.getType())) {
119: execute(evt);
120: }
121: }
122:
123: /** Generate configuration files. Override with method to generate
124: web server specific configuration.
125: */
126: public void execute(LifecycleEvent evt) {
127: initProperties();
128: PrintWriter mod_jk = null;
129: try {
130: mod_jk = getWriter();
131: } catch (IOException iex) {
132: log("Unable to open config file");
133: return;
134: }
135: Lifecycle who = evt.getLifecycle();
136: if (who instanceof Server) {
137: executeServer((Server) who, mod_jk);
138: } else if (who instanceof Host) {
139: executeHost((Host) who, mod_jk);
140: } else if (who instanceof Context) {
141: executeContext((Context) who, mod_jk);
142: }
143: mod_jk.close();
144: }
145:
146: /** Generate configuration files. Override with method to generate
147: web server specific configuration.
148: */
149: public void executeServer(Server svr, PrintWriter mod_jk) {
150: if (!append) {
151: if (!generateJkHead(mod_jk))
152: return;
153: generateSSLConfig(mod_jk);
154: generateJkTail(mod_jk);
155: }
156: }
157:
158: /** Generate SSL options
159: */
160: protected void generateSSLConfig(PrintWriter mod_jk) {
161: }
162:
163: /** Generate general options
164: */
165: protected boolean generateJkHead(PrintWriter mod_jk) {
166: return true;
167: }
168:
169: /** Generate general options
170: */
171: protected void generateJkTail(PrintWriter mod_jk) {
172: }
173:
174: /** Generate Virtual Host start
175: */
176: protected void generateVhostHead(Host host, PrintWriter mod_jk) {
177: }
178:
179: /** Generate Virtual Host end
180: */
181: protected void generateVhostTail(Host host, PrintWriter mod_jk) {
182: }
183:
184: /** Generate configuration files. Override with method to generate
185: web server specific configuration.
186: */
187: protected void executeEngine(Engine egn, PrintWriter mod_jk) {
188: Container[] children = egn.findChildren();
189: for (int ii = 0; ii < children.length; ii++) {
190: if (children[ii] instanceof Host) {
191: executeHost((Host) children[ii], mod_jk);
192: } else if (children[ii] instanceof Context) {
193: executeContext((Context) children[ii], mod_jk);
194: }
195: }
196: }
197:
198: /** Generate configuration files. Override with method to generate
199: web server specific configuration.
200: */
201: protected void executeHost(Host hst, PrintWriter mod_jk) {
202: generateVhostHead(hst, mod_jk);
203: Container[] children = hst.findChildren();
204: for (int ii = 0; ii < children.length; ii++) {
205: if (children[ii] instanceof Context) {
206: executeContext((Context) children[ii], mod_jk);
207: }
208: }
209: generateVhostTail(hst, mod_jk);
210: }
211:
212: /**
213: executes the ApacheConfig interceptor. This method generates apache
214: configuration files for use with mod_jk.
215: <p>
216: @param <b>context</b> a Context object.
217: @param <b>mod_jk</b> Writer for output.
218: */
219: public void executeContext(Context context, PrintWriter mod_jk) {
220:
221: if (context.getPath().length() > 0 || !noRoot) {
222: String docRoot = context.getServletContext().getRealPath(
223: "/");
224: if (forwardAll || docRoot == null)
225: generateStupidMappings(context, mod_jk);
226: else
227: generateContextMappings(context, mod_jk);
228: }
229: }
230:
231: protected void generateStupidMappings(Context context,
232: PrintWriter mod_jk) {
233: }
234:
235: protected void generateContextMappings(Context context,
236: PrintWriter mod_jk) {
237: }
238:
239: /** Get the output Writer. Override with method to generate
240: web server specific configuration.
241: */
242: protected PrintWriter getWriter() throws IOException {
243: return null;
244: }
245:
246: /** Get the host associated with this Container (if any).
247: */
248: protected Host getHost(Container child) {
249: while (child != null && !(child instanceof Host)) {
250: child = child.getParent();
251: }
252: return (Host) child;
253: }
254:
255: //-------------------- Properties --------------------
256:
257: /** Append to config file.
258: * Set to <code>true</code> if the config information should be
259: * appended.
260: */
261: public void setAppend(boolean apnd) {
262: append = apnd;
263: }
264:
265: /** If false, we'll try to generate a config that will
266: * let apache serve static files.
267: * The default is true, forward all requests in a context
268: * to tomcat.
269: */
270: public void setForwardAll(boolean b) {
271: forwardAll = b;
272: }
273:
274: /** Special option - do not generate mappings for the ROOT
275: context. The default is true, and will not generate the mappings,
276: not redirecting all pages to tomcat (since /* matches everything).
277: This means that the web server's root remains intact but isn't
278: completely servlet/JSP enabled. If the ROOT webapp can be configured
279: with the web server serving static files, there's no problem setting
280: this option to false. If not, then setting it true means the web
281: server will be out of picture for all requests.
282: */
283: public void setNoRoot(boolean b) {
284: noRoot = b;
285: }
286:
287: /**
288: set a path to the parent directory of the
289: conf folder. That is, the parent directory
290: within which path setters would be resolved against,
291: if relative. For example if ConfigHome is set to "/home/tomcat"
292: and regConfig is set to "conf/mod_jk.conf" then the resulting
293: path used would be:
294: "/home/tomcat/conf/mod_jk.conf".</p>
295: <p>
296: However, if the path is set to an absolute path,
297: this attribute is ignored.
298: <p>
299: If not set, execute() will set this to TOMCAT_HOME.
300: <p>
301: @param <b>dir</b> - path to a directory
302: */
303: public void setConfigHome(String dir) {
304: if (dir == null)
305: return;
306: File f = new File(dir);
307: if (!f.isDirectory()) {
308: throw new IllegalArgumentException(
309: "BaseConfig.setConfigHome(): "
310: + "Configuration Home must be a directory! : "
311: + dir);
312: }
313: configHome = f;
314: }
315:
316: /**
317: set a path to the workers.properties file.
318: @param <b>path</b> String path to workers.properties file
319: */
320: public void setWorkersConfig(String path) {
321: workersConfig = (path == null ? null : new File(path));
322: }
323:
324: /**
325: set the path to the log file
326: @param <b>path</b> String path to a file
327: */
328: public void setJkLog(String path) {
329: jkLog = (path == null ? null : new File(path));
330: }
331:
332: /** Set the verbosity level
333: ( use debug, error, etc. ) If not set, no log is written.
334: */
335: public void setJkDebug(String level) {
336: jkDebug = level;
337: }
338:
339: /**
340: set the Ajp protocal
341: @param <b>protocal</b> String protocol, "ajp12" or "ajp13"
342: */
343: public void setJkWorker(String worker) {
344: jkWorker = worker;
345: }
346:
347: public void setLegacy(boolean legacy) {
348: this .legacy = legacy;
349: }
350:
351: // -------------------- Initialize/guess defaults --------------------
352:
353: /** Initialize defaults for properties that are not set
354: explicitely
355: */
356: protected void initProperties() {
357: tomcatHome = System.getProperty("catalina.home");
358: File tomcatDir = new File(tomcatHome);
359: if (configHome == null) {
360: configHome = tomcatDir;
361: }
362: }
363:
364: // -------------------- Config Utils --------------------
365:
366: /** Add an extension mapping. Override with method to generate
367: web server specific configuration
368: */
369: protected boolean addExtensionMapping(String ctxPath, String ext,
370: PrintWriter pw) {
371: return true;
372: }
373:
374: /** Add a fulling specified mapping. Override with method to generate
375: web server specific configuration
376: */
377: protected boolean addMapping(String fullPath, PrintWriter pw) {
378: return true;
379: }
380:
381: // -------------------- General Utils --------------------
382:
383: protected String getAbsoluteDocBase(Context context) {
384: // Calculate the absolute path of the document base
385: String docBase = context.getServletContext().getRealPath("/");
386: docBase = docBase.substring(0, docBase.length() - 1);
387: if (!isAbsolute(docBase)) {
388: docBase = tomcatHome + "/" + docBase;
389: }
390: docBase = patch(docBase);
391: return docBase;
392: }
393:
394: // ------------------ Grabbed from FileUtil -----------------
395: public static File getConfigFile(File base, File configDir,
396: String defaultF) {
397: if (base == null)
398: base = new File(defaultF);
399: if (!base.isAbsolute()) {
400: if (configDir != null)
401: base = new File(configDir, base.getPath());
402: else
403: base = new File(base.getAbsolutePath()); //??
404: }
405: File parent = new File(base.getParent());
406: if (!parent.exists()) {
407: if (!parent.mkdirs()) {
408: throw new RuntimeException(
409: "Unable to create path to config file :"
410: + base.getAbsolutePath());
411: }
412: }
413: return base;
414: }
415:
416: public static String patch(String path) {
417: String patchPath = path;
418:
419: // Move drive spec to the front of the path
420: if (patchPath.length() >= 3 && patchPath.charAt(0) == '/'
421: && Character.isLetter(patchPath.charAt(1))
422: && patchPath.charAt(2) == ':') {
423: patchPath = patchPath.substring(1, 3) + "/"
424: + patchPath.substring(3);
425: }
426:
427: // Eliminate consecutive slashes after the drive spec
428: if (patchPath.length() >= 2
429: && Character.isLetter(patchPath.charAt(0))
430: && patchPath.charAt(1) == ':') {
431: char[] ca = patchPath.replace('/', '\\').toCharArray();
432: char c;
433: StringBuffer sb = new StringBuffer();
434:
435: for (int i = 0; i < ca.length; i++) {
436: if ((ca[i] != '\\')
437: || (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
438: if (i == 0 && Character.isLetter(ca[i])
439: && i < ca.length - 1 && ca[i + 1] == ':') {
440: c = Character.toUpperCase(ca[i]);
441: } else {
442: c = ca[i];
443: }
444:
445: sb.append(c);
446: }
447: }
448:
449: patchPath = sb.toString();
450: }
451:
452: // fix path on NetWare - all '/' become '\\' and remove duplicate '\\'
453: if (System.getProperty("os.name").startsWith("NetWare")
454: && path.length() >= 3 && path.indexOf(':') > 0) {
455: char[] ca = patchPath.replace('/', '\\').toCharArray();
456: StringBuffer sb = new StringBuffer();
457:
458: for (int i = 0; i < ca.length; i++) {
459: if ((ca[i] != '\\')
460: || (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
461: sb.append(ca[i]);
462: }
463: }
464: patchPath = sb.toString();
465: }
466:
467: return patchPath;
468: }
469:
470: public static boolean isAbsolute(String path) {
471: // normal file
472: if (path.startsWith("/"))
473: return true;
474:
475: if (path.startsWith(File.separator))
476: return true;
477:
478: // win c:
479: if (path.length() >= 3 && Character.isLetter(path.charAt(0))
480: && path.charAt(1) == ':')
481: return true;
482:
483: // NetWare volume:
484: if (System.getProperty("os.name").startsWith("NetWare")
485: && path.length() >= 3 && path.indexOf(':') > 0)
486: return true;
487:
488: return false;
489: }
490:
491: protected void log(String msg) {
492: System.err.println(msg);
493: }
494: }
|