Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/ajp/tomcat4/config/ApacheConfig.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.FileWriter;
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.util.Date;
24  import java.util.Hashtable;
25  
26  import org.apache.catalina.Context;
27  import org.apache.catalina.Host;
28  
29  /* The idea is to keep all configuration in server.xml and
30     the normal apache config files. We don't want people to
31     touch apache ( or IIS, NES ) config files unless they
32     want to and know what they're doing ( better than we do :-).
33  
34     One nice feature ( if someone sends it ) would be to
35     also edit httpd.conf to add the include.
36  
37     We'll generate a number of configuration files - this one
38     is trying to generate a native apache config file.
39  
40     Some web.xml mappings do not "map" to server configuration - in
41     this case we need to fallback to forward all requests to tomcat.
42  
43     Ajp14 will add to that the posibility to have tomcat and
44     apache on different machines, and many other improvements -
45     but this should also work for Ajp12, Ajp13 and Jni.
46  
47  */
48  
49  /**
50      Generates automatic apache mod_jk configurations based on
51      the Tomcat server.xml settings and the war contexts
52      initialized during startup.
53      <p>
54      This config interceptor is enabled by inserting an ApacheConfig
55      <code>Listener</code> in 
56      the server.xml file like so:
57      <pre>
58      * < Server ... >
59      *   ...
60      *   <Listener className=<b>org.apache.ajp.tomcat4.config.ApacheConfig</b> 
61      *       <i>options</i> />
62      *   ...
63      * < /Server >
64      </pre>
65      where <i>options</i> can include any of the following attributes:
66      <ul>
67       <li><b>configHome</b> - default parent directory for the following paths.
68                              If not set, this defaults to TOMCAT_HOME. Ignored
69                              whenever any of the following paths is absolute.
70                               </li>
71       <li><b>jkConfig</b> - path to use for writing Apache mod_jk conf file. If
72                              not set, defaults to
73                              "conf/auto/mod_jk.conf".</li>
74       <li><b>workersConfig</b> - path to workers.properties file used by 
75                              mod_jk. If not set, defaults to
76                              "conf/jk/workers.properties".</li>
77       <li><b>modJk</b> - path to Apache mod_jk plugin file.  If not set,
78                          defaults to "modules/mod_jk.dll" on windows,
79                          "modules/mod_jk.nlm" on netware, and
80                          "libexec/mod_jk.so" everywhere else.</li>
81       <li><b>jkLog</b> - path to log file to be used by mod_jk.</li>
82       <li><b>jkDebug</b> - JK Loglevel setting.  May be debug, info, error, or emerg.
83                            If not set, defaults to emerg.</li>
84       <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
85                           defined in the workers.properties file. "ajp12", "ajp13"
86                           or "inprocess" are the workers found in the default
87                           workers.properties file. If not specified, defaults
88                           to "ajp13" if an Ajp13Interceptor is in use, otherwise
89                           it defaults to "ajp12".</li>
90       <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
91                               insure that all the behavior configured in the web.xml
92                               file functions correctly.  If false, let Apache serve
93                               static resources. The default is true.
94                               Warning: When false, some configuration in
95                               the web.xml may not be duplicated in Apache.
96                               Review the mod_jk conf file to see what
97                               configuration is actually being set in Apache.</li>
98       <li><b>noRoot</b> - If true, the root context is not mapped to
99                           Tomcat.  If false and forwardAll is true, all requests
100                          to the root context are mapped to Tomcat. If false and
101                          forwardAll is false, only JSP and servlets requests to
102                          the root context are mapped to Tomcat. When false,
103                          to correctly serve Tomcat's root context you must also
104                          modify the DocumentRoot setting in Apache's httpd.conf
105                          file to point to Tomcat's root context directory.
106                          Otherwise some content, such as Apache's index.html,
107                          will be served by Apache before mod_jk gets a chance
108                          to claim the request and pass it to Tomcat.
109                          The default is true.</li>
110     </ul>
111     <p>
112     @author Costin Manolache
113     @author Larry Isaacs
114     @author Mel Martinez
115     @author Bill Barker
116  */
117 public class ApacheConfig  extends BaseJkConfig { 
118     
119     /** default path to mod_jk .conf location */
120     public static final String MOD_JK_CONFIG = "conf/auto/mod_jk.conf";
121     /** default path to workers.properties file
122   This should be also auto-generated from server.xml.
123     */
124     public static final String WORKERS_CONFIG = "conf/jk/workers.properties";
125     /** default mod_jk log file location */
126     public static final String JK_LOG_LOCATION = "logs/mod_jk.log";
127     /** default location of mod_jk Apache plug-in. */
128     public static final String MOD_JK;
129     
130     //set up some defaults based on OS type
131     static{
132         String os = System.getProperty("os.name").toLowerCase();
133         if(os.indexOf("windows")>=0){ 
134            MOD_JK = "modules/mod_jk.dll";
135         }else if(os.indexOf("netware")>=0){
136            MOD_JK = "modules/mod_jk.nlm";
137         }else{
138            MOD_JK = "libexec/mod_jk.so";
139         }
140     }
141     
142     private File jkConfig = null;
143     private File modJk = null;
144 
145     // ssl settings 
146     private boolean sslExtract=true;
147     private String sslHttpsIndicator="HTTPS";
148     private String sslSessionIndicator="SSL_SESSION_ID";
149     private String sslCipherIndicator="SSL_CIPHER";
150     private String sslCertsIndicator="SSL_CLIENT_CERT";
151 
152     Hashtable NamedVirtualHosts=null;
153     
154     public ApacheConfig() {
155     }
156 
157     //-------------------- Properties --------------------
158 
159     /**
160         set the path to the output file for the auto-generated
161         mod_jk configuration file.  If this path is relative
162         then it will be resolved absolutely against
163         the getConfigHome() path.
164         <p>
165         @param path String path to a file
166     */
167     public void setJkConfig(String path){
168   jkConfig= (path==null)?null:new File(path);
169     }
170 
171     /**
172         set the path to the mod_jk Apache Module
173         @param path String path to a file
174     */
175     public void setModJk(String path){
176         modJk=( path==null?null:new File(path));
177     }
178 
179     /** By default mod_jk is configured to collect SSL information from
180   the apache environment and send it to the Tomcat workers. The
181   problem is that there are many SSL solutions for Apache and as
182   a result the environment variable names may change.
183 
184   The following JK related SSL configureation
185   can be used to customize mod_jk's SSL behaviour.
186 
187   Should mod_jk send SSL information to Tomact (default is On)
188     */
189     public void setExtractSSL( boolean sslMode ) {
190   this.sslExtract=sslMode;
191     }
192 
193     /** What is the indicator for SSL (default is HTTPS)
194      */
195     public void setHttpsIndicator( String s ) {
196   sslHttpsIndicator=s;
197     }
198 
199     /**What is the indicator for SSL session (default is SSL_SESSION_ID)
200      */
201     public void setSessionIndicator( String s ) {
202   sslSessionIndicator=s;
203     }
204     
205     /**What is the indicator for client SSL cipher suit (default is SSL_CIPHER)
206      */
207     public void setCipherIndicator( String s ) {
208   sslCipherIndicator=s;
209     }
210 
211     /** What is the indicator for the client SSL certificated(default
212   is SSL_CLIENT_CERT
213      */
214     public void setCertsIndicator( String s ) {
215   sslCertsIndicator=s;
216     }
217 
218     // -------------------- Initialize/guess defaults --------------------
219 
220     /** Initialize defaults for properties that are not set
221   explicitely
222     */
223     protected void initProperties() {
224         super.initProperties();
225 
226   jkConfig= getConfigFile( jkConfig, configHome, MOD_JK_CONFIG);
227   workersConfig=getConfigFile( workersConfig, configHome,
228              WORKERS_CONFIG);
229   if( modJk == null )
230       modJk=new File(MOD_JK);
231   else
232       modJk=getConfigFile( modJk, configHome, MOD_JK );
233   jkLog=getConfigFile( jkLog, configHome, JK_LOG_LOCATION);
234     }
235     // -------------------- Generate config --------------------
236     
237     protected PrintWriter getWriter() throws IOException {
238   String abJkConfig = jkConfig.getAbsolutePath();
239   return new PrintWriter(new FileWriter(abJkConfig, append));
240     }
241              
242 
243     // -------------------- Config sections  --------------------
244 
245     /** Generate the loadModule and general options
246      */
247     protected boolean generateJkHead(PrintWriter mod_jk)
248     {
249 
250   mod_jk.println("########## Auto generated on " +  new Date() +
251            "##########" );
252   mod_jk.println();
253 
254   // Fail if mod_jk not found, let the user know the problem
255   // instead of running into problems later.
256   if( ! modJk.exists() ) {
257       log( "mod_jk location: " + modJk );
258       log( "Make sure it is installed corectly or " +
259      " set the config location" );
260       log( "Using <Listener className=\""+getClass().getName()+"\"  modJk=\"PATH_TO_MOD_JK.SO_OR_DLL\" />" );
261   }
262             
263   // Verify the file exists !!
264   mod_jk.println("<IfModule !mod_jk.c>");
265   mod_jk.println("  LoadModule jk_module \""+
266            modJk.toString().replace('\\','/') +
267                        "\"");
268   mod_jk.println("</IfModule>");
269   mod_jk.println();                
270 
271   
272   // Fail if workers file not found, let the user know the problem
273   // instead of running into problems later.
274   if( ! workersConfig.exists() ) {
275       log( "Can't find workers.properties at " + workersConfig );
276       log( "Please install it in the default location or " +
277      " set the config location" );
278       log( "Using <Listener className=\"" + getClass().getName() + "\"  workersConfig=\"FULL_PATH\" />" );
279       return false;
280   }
281             
282   mod_jk.println("JkWorkersFile \"" 
283            + workersConfig.toString().replace('\\', '/') 
284            + "\"");
285 
286   mod_jk.println("JkLogFile \"" 
287            + jkLog.toString().replace('\\', '/') 
288            + "\"");
289   mod_jk.println();
290 
291   if( jkDebug != null ) {
292       mod_jk.println("JkLogLevel " + jkDebug);
293       mod_jk.println();
294   }
295   return true;
296     }
297 
298     protected void generateVhostHead(Host host, PrintWriter mod_jk) {
299 
300         mod_jk.println();
301         String vhostip = host.getName();
302   String vhost = vhostip;
303   int ppos = vhost.indexOf(":");
304   if(ppos >= 0)
305       vhost = vhost.substring(0,ppos);
306 
307         mod_jk.println("<VirtualHost "+ vhostip + ">");
308         mod_jk.println("    ServerName " + vhost );
309         String [] aliases=host.findAliases();
310         if( aliases.length > 0 ) {
311             mod_jk.print("    ServerAlias " );
312             for( int ii=0; ii < aliases.length ; ii++) {
313                 mod_jk.print( aliases[ii] + " " );
314             }
315             mod_jk.println();
316         }
317         indent="    ";
318     }
319 
320     protected void generateVhostTail(Host host, PrintWriter mod_jk) {
321         mod_jk.println("</VirtualHost>");
322         indent="";
323     }
324     
325     protected void generateSSLConfig(PrintWriter mod_jk) {
326   if( ! sslExtract ) {
327       mod_jk.println("JkExtractSSL Off");        
328   }
329   if( ! "HTTPS".equalsIgnoreCase( sslHttpsIndicator ) ) {
330       mod_jk.println("JkHTTPSIndicator " + sslHttpsIndicator);        
331   }
332   if( ! "SSL_SESSION_ID".equalsIgnoreCase( sslSessionIndicator )) {
333       mod_jk.println("JkSESSIONIndicator " + sslSessionIndicator);
334   }
335   if( ! "SSL_CIPHER".equalsIgnoreCase( sslCipherIndicator )) {
336       mod_jk.println("JkCIPHERIndicator " + sslCipherIndicator);
337   }
338   if( ! "SSL_CLIENT_CERT".equalsIgnoreCase( sslCertsIndicator )) {
339       mod_jk.println("JkCERTSIndicator " + sslCertsIndicator);
340   }
341 
342   mod_jk.println();
343     }
344 
345     // -------------------- Forward all mode --------------------
346     String indent="";
347     
348     /** Forward all requests for a context to tomcat.
349   The default.
350      */
351     protected void generateStupidMappings(Context context,
352              PrintWriter mod_jk )
353     {
354   String ctxPath  = context.getPath();
355   if(ctxPath == null)
356       return;
357 
358   String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
359   
360         mod_jk.println();
361   mod_jk.println(indent + "JkMount " +  nPath + " " + jkWorker );
362   if( "".equals(ctxPath) ) {
363       mod_jk.println(indent + "JkMount " +  nPath + "* " + jkWorker );
364             if ( context.getParent() instanceof Host ) {
365                 mod_jk.println(indent + "DocumentRoot \"" +
366                             getApacheDocBase(context) + "\"");
367             } else {
368                 mod_jk.println(indent +
369                         "# To avoid Apache serving root welcome files from htdocs, update DocumentRoot");
370                 mod_jk.println(indent +
371                         "# to point to: \"" + getApacheDocBase(context) + "\"");
372             }
373 
374   } else {
375       mod_jk.println(indent + "JkMount " +  nPath + "/* " + jkWorker );
376   }
377     }    
378 
379     
380     private void generateNameVirtualHost( PrintWriter mod_jk, String ip ) {
381         if( !NamedVirtualHosts.containsKey(ip) ) {
382             mod_jk.println("NameVirtualHost " + ip + "");
383             NamedVirtualHosts.put(ip,ip);
384         }
385     }
386     
387     // -------------------- Apache serves static mode --------------------
388     // This is not going to work for all apps. We fall back to stupid mode.
389     
390     protected void generateContextMappings(Context context, PrintWriter mod_jk )
391     {
392   String ctxPath  = context.getPath();
393   Host vhost = getHost(context);
394 
395         if( noRoot &&  "".equals(ctxPath) ) {
396             log("Ignoring root context in non-forward-all mode  ");
397             return;
398         }
399 
400   mod_jk.println();
401   mod_jk.println(indent + "#################### " +
402            ((vhost!=null ) ? vhost.getName() + ":" : "" ) +
403            (("".equals(ctxPath)) ? "/" : ctxPath ) +
404            " ####################" );
405         mod_jk.println();
406   // Dynamic /servet pages go to Tomcat
407  
408   generateStaticMappings( context, mod_jk );
409 
410   // InvokerInterceptor - it doesn't have a container,
411   // but it's implemented using a special module.
412   
413   // XXX we need to better collect all mappings
414 
415   if(context.getLoginConfig() != null) {
416       String loginPage = context.getLoginConfig().getLoginPage();
417       if(loginPage != null) {
418     int lpos = loginPage.lastIndexOf("/");
419     String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
420     addMapping( ctxPath, jscurl, mod_jk);
421       }
422   }
423   String [] servletMaps = context.findServletMappings();
424   for(int ii=0; ii < servletMaps.length; ii++) {
425         addMapping( ctxPath, servletMaps[ii] , mod_jk );
426   }
427     }
428 
429     /** Add an Apache extension mapping.
430      */
431     protected boolean addExtensionMapping( String ctxPath, String ext,
432            PrintWriter mod_jk )
433     {
434         if( debug > 0 )
435             log( "Adding extension map for " + ctxPath + "/*." + ext );
436   mod_jk.println(indent + "JkMount " + ctxPath + "/*." + ext
437            + " " + jkWorker);
438   return true;
439     }
440     
441     
442     /** Add a fulling specified Appache mapping.
443      */
444     protected boolean addMapping( String fullPath, PrintWriter mod_jk ) {
445         if( debug > 0 )
446             log( "Adding map for " + fullPath );
447   mod_jk.println(indent + "JkMount " + fullPath + "  " + jkWorker );
448   return true;
449     }
450     /** Add a partially specified Appache mapping.
451      */
452     protected boolean addMapping( String ctxP, String ext, PrintWriter mod_jk ) {
453         if( debug > 0 )
454             log( "Adding map for " + ext );
455   if(! ext.startsWith("/") )
456       ext = "/" + ext;
457   if(ext.length() > 1)
458       mod_jk.println(indent + "JkMount " + ctxP + ext+ "  " + jkWorker );
459   return true;
460     }
461 
462     private void generateWelcomeFiles(Context context, PrintWriter mod_jk ) {
463   String wf[]=context.findWelcomeFiles();
464   if( wf==null || wf.length == 0 )
465       return;
466   mod_jk.print(indent + "    DirectoryIndex ");
467   for( int i=0; i<wf.length ; i++ ) {
468       mod_jk.print( wf[i] + " " );
469   }
470   mod_jk.println();
471     }
472 
473     /** Mappings for static content. XXX need to add welcome files,
474      *  mime mappings ( all will be handled by Mime and Static modules of
475      *  apache ).
476      */
477     private void generateStaticMappings(Context context, PrintWriter mod_jk ) {
478   String ctxPath  = context.getPath();
479 
480   // Calculate the absolute path of the document base
481   String docBase = getApacheDocBase(context);
482 
483         if( !"".equals(ctxPath) ) {
484             // Static files will be served by Apache
485             mod_jk.println(indent + "# Static files ");        
486             mod_jk.println(indent + "Alias " + ctxPath + " \"" + docBase + "\"");
487             mod_jk.println();
488         } else {
489             if ( getHost(context) != null ) {
490                 mod_jk.println(indent + "DocumentRoot \"" +
491                             getApacheDocBase(context) + "\"");
492             } else {
493                 // For root context, ask user to update DocumentRoot setting.
494                 // Using "Alias / " interferes with the Alias for other contexts.
495                 mod_jk.println(indent +
496                         "# Be sure to update DocumentRoot");
497                 mod_jk.println(indent +
498                         "# to point to: \"" + docBase + "\"");
499             }
500         }
501   mod_jk.println(indent + "<Directory \"" + docBase + "\">");
502   mod_jk.println(indent + "    Options Indexes FollowSymLinks");
503 
504   generateWelcomeFiles(context, mod_jk);
505 
506   // XXX XXX Here goes the Mime types and welcome files !!!!!!!!
507   mod_jk.println(indent + "</Directory>");
508   mod_jk.println();            
509   
510 
511   // Deny serving any files from WEB-INF
512   mod_jk.println();            
513   mod_jk.println(indent +
514            "# Deny direct access to WEB-INF and META-INF");
515   mod_jk.println(indent + "#");                        
516   mod_jk.println(indent + "<Location \"" + ctxPath + "/WEB-INF/*\">");
517   mod_jk.println(indent + "    AllowOverride None");
518   mod_jk.println(indent + "    deny from all");
519   mod_jk.println(indent + "</Location>");
520   // Deny serving any files from META-INF
521   mod_jk.println();            
522   mod_jk.println(indent + "<Location \"" + ctxPath + "/META-INF/*\">");
523   mod_jk.println(indent + "    AllowOverride None");
524   mod_jk.println(indent + "    deny from all");
525   mod_jk.println(indent + "</Location>");
526   if (File.separatorChar == '\\') {
527       mod_jk.println(indent + "#");        
528       mod_jk.println(indent +
529          "# Use Directory too. On Windows, Location doesn't"
530          + " work unless case matches");
531       mod_jk.println(indent + "#");                        
532       mod_jk.println(indent +
533          "<Directory \"" + docBase + "/WEB-INF/\">");
534       mod_jk.println(indent + "    AllowOverride None");
535       mod_jk.println(indent + "    deny from all");
536       mod_jk.println(indent + "</Directory>");
537       mod_jk.println();
538       mod_jk.println(indent +
539          "<Directory \"" + docBase + "/META-INF/\">");
540       mod_jk.println(indent + "    AllowOverride None");
541       mod_jk.println(indent + "    deny from all");
542       mod_jk.println(indent + "</Directory>");
543   }
544   mod_jk.println();
545     }    
546 
547     // -------------------- Utils --------------------
548 
549     private String getApacheDocBase(Context context)
550     {
551   // Calculate the absolute path of the document base
552   String docBase = getAbsoluteDocBase(context);
553   if (File.separatorChar == '\\') {
554       // use separator preferred by Apache
555       docBase = docBase.replace('\\','/');
556   }
557         return docBase;
558     }
559 
560     private String getVirtualHostAddress(String vhost, String vhostip) {
561         if( vhostip == null ) {
562             if ( vhost != null && vhost.length() > 0 && Character.isDigit(vhost.charAt(0)) )
563                 vhostip=vhost;
564             else
565                 vhostip="*";
566         }
567         return vhostip;
568     }
569 
570 }