Source code: org/apache/ajp/tomcat4/config/BaseJkConfig.java
1 /*
2 * Copyright 1999-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.ajp.tomcat4.config;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.PrintWriter;
22
23 import org.apache.catalina.Container;
24 import org.apache.catalina.Context;
25 import org.apache.catalina.Engine;
26 import org.apache.catalina.Host;
27 import org.apache.catalina.Lifecycle;
28 import org.apache.catalina.LifecycleEvent;
29 import org.apache.catalina.LifecycleListener;
30 import org.apache.catalina.Server;
31
32
33 /**
34 Base class for automatic jk based configurations based on
35 the Tomcat server.xml settings and the war contexts
36 initialized during startup.
37 <p>
38 This config interceptor is enabled by inserting a Config
39 element in the <b><ContextManager></b> tag body inside
40 the server.xml file like so:
41 <pre>
42 * < ContextManager ... >
43 * ...
44 * <<b>???Config</b> <i>options</i> />
45 * ...
46 * < /ContextManager >
47 </pre>
48 where <i>options</i> can include any of the following attributes:
49 <ul>
50 <li><b>configHome</b> - default parent directory for the following paths.
51 If not set, this defaults to TOMCAT_HOME. Ignored
52 whenever any of the following paths is absolute.
53 </li>
54 <li><b>workersConfig</b> - path to workers.properties file used by
55 jk connector. If not set, defaults to
56 "conf/jk/workers.properties".</li>
57 <li><b>jkLog</b> - path to log file to be used by jk connector.</li>
58 <li><b>jkDebug</b> - Loglevel setting. May be debug, info, error, or emerg.
59 If not set, defaults to emerg.</li>
60 <li><b>jkWorker</b> The desired worker. Must be set to one of the workers
61 defined in the workers.properties file. "ajp12", "ajp13"
62 or "inprocess" are the workers found in the default
63 workers.properties file. If not specified, defaults
64 to "ajp13" if an Ajp13Interceptor is in use, otherwise
65 it defaults to "ajp12".</li>
66 <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
67 insure that all the behavior configured in the web.xml
68 file functions correctly. If false, let Apache serve
69 static resources. The default is true.
70 Warning: When false, some configuration in
71 the web.xml may not be duplicated in Apache.
72 Review the mod_jk conf file to see what
73 configuration is actually being set in Apache.</li>
74 <li><b>noRoot</b> - If true, the root context is not mapped to
75 Tomcat. If false and forwardAll is true, all requests
76 to the root context are mapped to Tomcat. If false and
77 forwardAll is false, only JSP and servlets requests to
78 the root context are mapped to Tomcat. When false,
79 to correctly serve Tomcat's root context you may also
80 need to modify the web server to point it's home
81 directory to Tomcat's root context directory.
82 Otherwise some content, such as the root index.html,
83 may be served by the web server before the connector
84 gets a chance to claim the request and pass it to Tomcat.
85 The default is true.</li>
86 </ul>
87 <p>
88 @author Costin Manolache
89 @author Larry Isaacs
90 @author Bill Barker
91 @version $Revision: 299797 $
92 */
93 public class BaseJkConfig implements LifecycleListener {
94
95 private static org.apache.commons.logging.Log log=
96 org.apache.commons.logging.LogFactory.getLog( BaseJkConfig.class );
97
98 protected int debug=0;
99 protected File configHome = null;
100 protected File workersConfig = null;
101
102 protected File jkLog = null;
103 protected String jkDebug="emerg";
104 protected String jkWorker = "ajp13";
105
106 protected boolean noRoot=true;
107 protected boolean forwardAll=true;
108
109 protected String tomcatHome;
110 protected boolean regenerate=false;
111 protected boolean append=false;
112
113 // -------------------- Tomcat callbacks --------------------
114
115
116 // Auto-config should be able to react to dynamic config changes,
117 // and regenerate the config.
118
119 /** Generate the configuration - only when the server is
120 * completely initialized ( before starting )
121 */
122 public void lifecycleEvent(LifecycleEvent evt)
123 {
124 if(Lifecycle.START_EVENT.equals(evt.getType())) {
125 execute( evt );
126 }
127 }
128
129 /** Generate configuration files. Override with method to generate
130 web server specific configuration.
131 */
132 public void execute(LifecycleEvent evt)
133 {
134 initProperties();
135 PrintWriter mod_jk = null;
136 try {
137 mod_jk = getWriter();
138 } catch(IOException iex) {
139 log.warn("Unable to open config file", iex);
140 return;
141 }
142 Lifecycle who = evt.getLifecycle();
143 if( who instanceof Server ) {
144 executeServer((Server)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 /** Generate configuration files. Override with method to generate
153 web server specific configuration.
154 */
155 public void executeServer(Server svr, PrintWriter mod_jk) {
156 if(! append ) {
157 if( ! generateJkHead(mod_jk) )
158 return;
159 generateSSLConfig(mod_jk);
160 generateJkTail(mod_jk);
161 }
162 }
163
164 /** Generate SSL options
165 */
166 protected void generateSSLConfig(PrintWriter mod_jk)
167 {
168 }
169 /** Generate general options
170 */
171 protected boolean generateJkHead(PrintWriter mod_jk)
172 {
173 return true;
174 }
175 /** Generate general options
176 */
177 protected void generateJkTail(PrintWriter mod_jk)
178 {
179 }
180 /** Generate Virtual Host start
181 */
182 protected void generateVhostHead(Host host, PrintWriter mod_jk) {
183 }
184 /** Generate Virtual Host end
185 */
186 protected void generateVhostTail(Host host, PrintWriter mod_jk) {
187 }
188 /** Generate configuration files. Override with method to generate
189 web server specific configuration.
190 */
191 protected void executeEngine(Engine egn, PrintWriter mod_jk) {
192 Container [] children = egn.findChildren();
193 for(int ii=0; ii < children.length; ii++) {
194 if( children[ii] instanceof Host ) {
195 executeHost((Host)children[ii], mod_jk);
196 } else if( children[ii] instanceof Context ) {
197 executeContext((Context)children[ii], mod_jk);
198 }
199 }
200 }
201 /** Generate configuration files. Override with method to generate
202 web server specific configuration.
203 */
204 protected void executeHost(Host hst, PrintWriter mod_jk) {
205 generateVhostHead(hst, mod_jk);
206 Container [] children = hst.findChildren();
207 for(int ii=0; ii < children.length; ii++) {
208 if(children[ii] instanceof Context) {
209 executeContext((Context)children[ii],mod_jk);
210 }
211 }
212 generateVhostTail(hst, mod_jk);
213 }
214 /**
215 executes the ApacheConfig interceptor. This method generates apache
216 configuration files for use with mod_jk.
217 <p>
218 @param context a Context object.
219 @param mod_jk Writer for output.
220 */
221 public void executeContext(Context context, PrintWriter mod_jk){
222
223 if(context.getPath().length() > 0 || ! noRoot ) {
224 String docRoot = context.getServletContext().getRealPath("/");
225 if( forwardAll || docRoot == null)
226 generateStupidMappings( context, mod_jk );
227 else
228 generateContextMappings( context, mod_jk);
229 }
230 }
231 protected void generateStupidMappings(Context context, PrintWriter mod_jk){
232 }
233 protected void generateContextMappings(Context context, PrintWriter mod_jk){
234 }
235 /** Get the output Writer. Override with method to generate
236 web server specific configuration.
237 */
238 protected PrintWriter getWriter() throws IOException {
239 return null;
240 }
241 /** Get the host associated with this Container (if any).
242 */
243 protected Host getHost(Container child) {
244 while(child != null && ! (child instanceof Host) ) {
245 child = child.getParent();
246 }
247 return (Host)child;
248 }
249
250 //-------------------- Properties --------------------
251
252 /** Append to config file.
253 * Set to <code>true</code> if the config information should be
254 * appended.
255 */
256 public void setAppend(boolean apnd) {
257 append = apnd;
258 }
259 /** If false, we'll try to generate a config that will
260 * let apache serve static files.
261 * The default is true, forward all requests in a context
262 * to tomcat.
263 */
264 public void setForwardAll( boolean b ) {
265 forwardAll=b;
266 }
267
268 /** Special option - do not generate mappings for the ROOT
269 context. The default is true, and will not generate the mappings,
270 not redirecting all pages to tomcat (since /* matches everything).
271 This means that the web server's root remains intact but isn't
272 completely servlet/JSP enabled. If the ROOT webapp can be configured
273 with the web server serving static files, there's no problem setting
274 this option to false. If not, then setting it true means the web
275 server will be out of picture for all requests.
276 */
277 public void setNoRoot( boolean b ) {
278 noRoot=b;
279 }
280
281 /**
282 set a path to the parent directory of the
283 conf folder. That is, the parent directory
284 within which path setters would be resolved against,
285 if relative. For example if ConfigHome is set to "/home/tomcat"
286 and regConfig is set to "conf/mod_jk.conf" then the resulting
287 path used would be:
288 "/home/tomcat/conf/mod_jk.conf".</p>
289 <p>
290 However, if the path is set to an absolute path,
291 this attribute is ignored.
292 <p>
293 If not set, execute() will set this to TOMCAT_HOME.
294 <p>
295 @param dir - path to a directory
296 */
297 public void setConfigHome(String dir){
298 if( dir==null ) return;
299 File f=new File(dir);
300 if(!f.isDirectory()){
301 throw new IllegalArgumentException(
302 "BaseConfig.setConfigHome(): "+
303 "Configuration Home must be a directory! : "+dir);
304 }
305 configHome = f;
306 }
307
308 /**
309 set a path to the workers.properties file.
310 @param path String path to workers.properties file
311 */
312 public void setWorkersConfig(String path){
313 workersConfig= (path==null?null:new File(path));
314 }
315
316 /**
317 set the path to the log file
318 @param path String path to a file
319 */
320 public void setJkLog(String path){
321 jkLog= ( path==null?null:new File(path));
322 }
323
324 /** Set the verbosity level
325 ( use debug, error, etc. ) If not set, no log is written.
326 */
327 public void setJkDebug( String level ) {
328 jkDebug=level;
329 }
330
331 /**
332 Set the AJP worker.
333 @param worker The worker name
334 */
335 public void setJkWorker(String worker){
336 jkWorker = worker;
337 }
338
339 // -------------------- Initialize/guess defaults --------------------
340
341 /** Initialize defaults for properties that are not set
342 explicitely
343 */
344 protected void initProperties() {
345 tomcatHome = System.getProperty("catalina.home");
346 File tomcatDir = new File(tomcatHome);
347 if(configHome==null){
348 configHome=tomcatDir;
349 }
350 }
351
352 // -------------------- Config Utils --------------------
353
354
355 /** Add an extension mapping. Override with method to generate
356 web server specific configuration
357 */
358 protected boolean addExtensionMapping( String ctxPath, String ext,
359 PrintWriter pw )
360 {
361 return true;
362 }
363
364
365 /** Add a fulling specified mapping. Override with method to generate
366 web server specific configuration
367 */
368 protected boolean addMapping( String fullPath, PrintWriter pw ) {
369 return true;
370 }
371
372 // -------------------- General Utils --------------------
373
374 protected String getAbsoluteDocBase(Context context)
375 {
376 // Calculate the absolute path of the document base
377 String docBase = context.getServletContext().getRealPath("/");
378 docBase = docBase.substring(0,docBase.length()-1);
379 if (!isAbsolute(docBase)){
380 docBase = tomcatHome + "/" + docBase;
381 }
382 docBase = patch(docBase);
383 return docBase;
384 }
385
386 // ------------------ Grabbed from FileUtil -----------------
387 public static File getConfigFile( File base, File configDir, String defaultF )
388 {
389 if( base==null )
390 base=new File( defaultF );
391 if( ! base.isAbsolute() ) {
392 if( configDir != null )
393 base=new File( configDir, base.getPath());
394 else
395 base=new File( base.getAbsolutePath()); //??
396 }
397 File parent=new File(base.getParent());
398 if(!parent.exists()){
399 if(!parent.mkdirs()){
400 throw new RuntimeException(
401 "Unable to create path to config file :"+
402 base.getAbsolutePath());
403 }
404 }
405 return base;
406 }
407 public static String patch(String path) {
408 String patchPath = path;
409
410 // Move drive spec to the front of the path
411 if (patchPath.length() >= 3 &&
412 patchPath.charAt(0) == '/' &&
413 Character.isLetter(patchPath.charAt(1)) &&
414 patchPath.charAt(2) == ':') {
415 patchPath=patchPath.substring(1,3)+"/"+patchPath.substring(3);
416 }
417
418 // Eliminate consecutive slashes after the drive spec
419 if (patchPath.length() >= 2 &&
420 Character.isLetter(patchPath.charAt(0)) &&
421 patchPath.charAt(1) == ':') {
422 char[] ca = patchPath.replace('/', '\\').toCharArray();
423 char c;
424 StringBuffer sb = new StringBuffer();
425
426 for (int i = 0; i < ca.length; i++) {
427 if ((ca[i] != '\\') ||
428 (ca[i] == '\\' &&
429 i > 0 &&
430 ca[i - 1] != '\\')) {
431 if (i == 0 &&
432 Character.isLetter(ca[i]) &&
433 i < ca.length - 1 &&
434 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 &&
450 path.indexOf(':') > 0) {
451 char[] ca = patchPath.replace('/', '\\').toCharArray();
452 StringBuffer sb = new StringBuffer();
453
454 for (int i = 0; i < ca.length; i++) {
455 if ((ca[i] != '\\') ||
456 (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
457 sb.append(ca[i]);
458 }
459 }
460 patchPath = sb.toString();
461 }
462
463 return patchPath;
464 }
465
466 public static boolean isAbsolute( String path ) {
467 // normal file
468 if( path.startsWith("/" ) ) return true;
469
470 if( path.startsWith(File.separator ) ) return true;
471
472 // win c:
473 if (path.length() >= 3 &&
474 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 &&
481 path.indexOf(':') > 0)
482 return true;
483
484 return false;
485 }
486
487 protected void log(String msg) {
488 log.info(msg);
489 }
490 }