| /* |
| * Copyright 1999-2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jk.config; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| |
| import org.apache.catalina.Container; |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Engine; |
| import org.apache.catalina.Host; |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleEvent; |
| import org.apache.catalina.LifecycleListener; |
| import org.apache.catalina.Server; |
| |
| |
| /** |
| Base class for automatic jk based configurations based on |
| the Tomcat server.xml settings and the war contexts |
| initialized during startup. |
| <p> |
| This config interceptor is enabled by inserting a Config |
| element in the <b><ContextManager></b> tag body inside |
| the server.xml file like so: |
| <pre> |
| * < ContextManager ... > |
| * ... |
| * <<b>???Config</b> <i>options</i> /> |
| * ... |
| * < /ContextManager > |
| </pre> |
| where <i>options</i> can include any of the following attributes: |
| <ul> |
| <li><b>configHome</b> - default parent directory for the following paths. |
| If not set, this defaults to TOMCAT_HOME. Ignored |
| whenever any of the following paths is absolute. |
| </li> |
| <li><b>workersConfig</b> - path to workers.properties file used by |
| jk connector. If not set, defaults to |
| "conf/jk/workers.properties".</li> |
| <li><b>jkLog</b> - path to log file to be used by jk connector.</li> |
| <li><b>jkDebug</b> - Loglevel setting. May be debug, info, error, or emerg. |
| If not set, defaults to emerg.</li> |
| <li><b>jkWorker</b> The desired worker. Must be set to one of the workers |
| defined in the workers.properties file. "ajp12", "ajp13" |
| or "inprocess" are the workers found in the default |
| workers.properties file. If not specified, defaults |
| to "ajp13" if an Ajp13Interceptor is in use, otherwise |
| it defaults to "ajp12".</li> |
| <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps |
| insure that all the behavior configured in the web.xml |
| file functions correctly. If false, let Apache serve |
| static resources. The default is true. |
| Warning: When false, some configuration in |
| the web.xml may not be duplicated in Apache. |
| Review the mod_jk conf file to see what |
| configuration is actually being set in Apache.</li> |
| <li><b>noRoot</b> - If true, the root context is not mapped to |
| Tomcat. If false and forwardAll is true, all requests |
| to the root context are mapped to Tomcat. If false and |
| forwardAll is false, only JSP and servlets requests to |
| the root context are mapped to Tomcat. When false, |
| to correctly serve Tomcat's root context you may also |
| need to modify the web server to point it's home |
| directory to Tomcat's root context directory. |
| Otherwise some content, such as the root index.html, |
| may be served by the web server before the connector |
| gets a chance to claim the request and pass it to Tomcat. |
| The default is true.</li> |
| </ul> |
| <p> |
| @author Costin Manolache |
| @author Larry Isaacs |
| @author Bill Barker |
| @version $Revision$ |
| */ |
| public class BaseJkConfig implements LifecycleListener { |
| private static org.apache.commons.logging.Log log = |
| org.apache.commons.logging.LogFactory.getLog(BaseJkConfig.class); |
| |
| protected File configHome = null; |
| protected File workersConfig = null; |
| |
| protected File jkLog = null; |
| protected String jkDebug="emerg"; |
| protected String jkWorker = "ajp13"; |
| |
| protected boolean noRoot=true; |
| protected boolean forwardAll=true; |
| |
| protected String tomcatHome; |
| protected boolean regenerate=false; |
| protected boolean append=false; |
| protected boolean legacy=true; |
| |
| // -------------------- Tomcat callbacks -------------------- |
| |
| |
| // Auto-config should be able to react to dynamic config changes, |
| // and regenerate the config. |
| |
| /** |
| * Generate the configuration - only when the server is |
| * completely initialized ( before starting ) |
| */ |
| public void lifecycleEvent(LifecycleEvent evt) { |
| if(Lifecycle.START_EVENT.equals(evt.getType())) { |
| execute( evt ); |
| } |
| } |
| |
| /** |
| * Generate configuration files. Override with method to generate |
| * web server specific configuration. |
| */ |
| public void execute(LifecycleEvent evt) { |
| initProperties(); |
| PrintWriter mod_jk = null; |
| try { |
| mod_jk = getWriter(); |
| } catch(IOException iex) { |
| log.warn("Unable to open config file"); |
| return; |
| } |
| Lifecycle who = evt.getLifecycle(); |
| if( who instanceof Server ) { |
| executeServer((Server)who, mod_jk); |
| } else if(who instanceof Engine) { |
| executeEngine((Engine)who, mod_jk); |
| } else if ( who instanceof Host ) { |
| executeHost((Host)who, mod_jk); |
| } else if( who instanceof Context ) { |
| executeContext((Context)who, mod_jk); |
| } |
| mod_jk.close(); |
| } |
| /** |
| * Generate configuration files. Override with method to generate |
| * web server specific configuration. |
| */ |
| public void executeServer(Server svr, PrintWriter mod_jk) { |
| if(! append ) { |
| if( ! generateJkHead(mod_jk) ) |
| return; |
| generateSSLConfig(mod_jk); |
| generateJkTail(mod_jk); |
| } |
| } |
| |
| /** |
| * Generate SSL options |
| */ |
| protected void generateSSLConfig(PrintWriter mod_jk) { |
| } |
| |
| /** |
| * Generate general options |
| */ |
| protected boolean generateJkHead(PrintWriter mod_jk) { |
| return true; |
| } |
| |
| /** |
| * Generate general options |
| */ |
| protected void generateJkTail(PrintWriter mod_jk) { |
| } |
| |
| /** |
| * Generate Virtual Host start |
| */ |
| protected void generateVhostHead(Host host, PrintWriter mod_jk) { |
| } |
| |
| /** |
| * Generate Virtual Host end |
| */ |
| protected void generateVhostTail(Host host, PrintWriter mod_jk) { |
| } |
| |
| /** |
| * Generate configuration files. Override with method to generate |
| * web server specific configuration. |
| */ |
| protected void executeEngine(Engine egn, PrintWriter mod_jk) { |
| if(egn.getJvmRoute() != null) { |
| jkWorker = egn.getJvmRoute(); |
| } |
| executeServer(egn.getService().getServer(), mod_jk); |
| Container [] children = egn.findChildren(); |
| for(int ii=0; ii < children.length; ii++) { |
| if( children[ii] instanceof Host ) { |
| executeHost((Host)children[ii], mod_jk); |
| } else if( children[ii] instanceof Context ) { |
| executeContext((Context)children[ii], mod_jk); |
| } |
| } |
| } |
| /** |
| * Generate configuration files. Override with method to generate |
| * web server specific configuration. |
| */ |
| protected void executeHost(Host hst, PrintWriter mod_jk) { |
| generateVhostHead(hst, mod_jk); |
| Container [] children = hst.findChildren(); |
| for(int ii=0; ii < children.length; ii++) { |
| if(children[ii] instanceof Context) { |
| executeContext((Context)children[ii],mod_jk); |
| } |
| } |
| generateVhostTail(hst, mod_jk); |
| } |
| /** |
| * executes the ApacheConfig interceptor. This method generates apache |
| * configuration files for use with mod_jk. |
| * @param context a Context object. |
| * @param mod_jk Writer for output. |
| */ |
| public void executeContext(Context context, PrintWriter mod_jk){ |
| |
| if(context.getPath().length() > 0 || ! noRoot ) { |
| String docRoot = context.getServletContext().getRealPath("/"); |
| if( forwardAll || docRoot == null) |
| generateStupidMappings( context, mod_jk ); |
| else |
| generateContextMappings( context, mod_jk); |
| } |
| } |
| |
| protected void generateStupidMappings(Context context, PrintWriter mod_jk){ |
| } |
| protected void generateContextMappings(Context context, PrintWriter mod_jk){ |
| } |
| |
| /** |
| * Get the output Writer. Override with method to generate |
| * web server specific configuration. |
| */ |
| protected PrintWriter getWriter() throws IOException { |
| return null; |
| } |
| |
| /** |
| * Get the host associated with this Container (if any). |
| */ |
| protected Host getHost(Container child) { |
| while(child != null && ! (child instanceof Host) ) { |
| child = child.getParent(); |
| } |
| return (Host)child; |
| } |
| |
| //-------------------- Properties -------------------- |
| |
| /** |
| * Append to config file. |
| * Set to <code>true</code> if the config information should be |
| * appended. |
| */ |
| public void setAppend(boolean apnd) { |
| append = apnd; |
| } |
| |
| /** |
| * If false, we'll try to generate a config that will |
| * let apache serve static files. |
| * The default is true, forward all requests in a context |
| * to tomcat. |
| */ |
| public void setForwardAll( boolean b ) { |
| forwardAll=b; |
| } |
| |
| /** |
| * Special option - do not generate mappings for the ROOT |
| * context. The default is true, and will not generate the mappings, |
| * not redirecting all pages to tomcat (since /* matches everything). |
| * This means that the web server's root remains intact but isn't |
| * completely servlet/JSP enabled. If the ROOT webapp can be configured |
| * with the web server serving static files, there's no problem setting |
| * this option to false. If not, then setting it true means the web |
| * server will be out of picture for all requests. |
| */ |
| public void setNoRoot( boolean b ) { |
| noRoot=b; |
| } |
| |
| /** |
| * set a path to the parent directory of the |
| * conf folder. That is, the parent directory |
| * within which path setters would be resolved against, |
| * if relative. For example if ConfigHome is set to "/home/tomcat" |
| * and regConfig is set to "conf/mod_jk.conf" then the resulting |
| * path used would be: |
| * "/home/tomcat/conf/mod_jk.conf".</p> |
| * <p> |
| * However, if the path is set to an absolute path, |
| * this attribute is ignored. |
| * <p> |
| * If not set, execute() will set this to TOMCAT_HOME. |
| * @param dir - path to a directory |
| */ |
| public void setConfigHome(String dir){ |
| if( dir==null ) return; |
| File f=new File(dir); |
| if(!f.isDirectory()){ |
| throw new IllegalArgumentException( |
| "BaseConfig.setConfigHome(): "+ |
| "Configuration Home must be a directory! : "+dir); |
| } |
| configHome = f; |
| } |
| |
| /** |
| * set a path to the workers.properties file. |
| * @param path String path to workers.properties file |
| */ |
| public void setWorkersConfig(String path){ |
| workersConfig= (path==null?null:new File(path)); |
| } |
| |
| /** |
| * set the path to the log file |
| * @param path String path to a file |
| */ |
| public void setJkLog(String path){ |
| jkLog = ( path==null ? null : new File(path)); |
| } |
| |
| /** |
| * Set the verbosity level |
| * ( use debug, error, etc. ) If not set, no log is written. |
| */ |
| public void setJkDebug( String level ) { |
| jkDebug=level; |
| } |
| |
| /** |
| * Sets the JK worker. |
| * @param worker The worker |
| */ |
| public void setJkWorker(String worker){ |
| jkWorker = worker; |
| } |
| |
| public void setLegacy(boolean legacy) { |
| this.legacy = legacy; |
| } |
| |
| // -------------------- Initialize/guess defaults -------------------- |
| |
| /** |
| * Initialize defaults for properties that are not set |
| * explicitely |
| */ |
| protected void initProperties() { |
| tomcatHome = System.getProperty("catalina.home"); |
| File tomcatDir = new File(tomcatHome); |
| if(configHome==null){ |
| configHome=tomcatDir; |
| } |
| } |
| |
| // -------------------- Config Utils -------------------- |
| |
| |
| /** |
| * Add an extension mapping. Override with method to generate |
| * web server specific configuration |
| */ |
| protected boolean addExtensionMapping( String ctxPath, String ext, |
| PrintWriter pw ) { |
| return true; |
| } |
| |
| |
| /** |
| * Add a fulling specified mapping. Override with method to generate |
| * web server specific configuration |
| */ |
| protected boolean addMapping( String fullPath, PrintWriter pw ) { |
| return true; |
| } |
| |
| // -------------------- General Utils -------------------- |
| |
| protected String getAbsoluteDocBase(Context context) { |
| // Calculate the absolute path of the document base |
| String docBase = context.getServletContext().getRealPath("/"); |
| docBase = docBase.substring(0,docBase.length()-1); |
| if (!isAbsolute(docBase)){ |
| docBase = tomcatHome + "/" + docBase; |
| } |
| docBase = patch(docBase); |
| return docBase; |
| } |
| |
| // ------------------ Grabbed from FileUtil ----------------- |
| public static File getConfigFile( File base, File configDir, String defaultF ) |
| { |
| if( base==null ) |
| base=new File( defaultF ); |
| if( ! base.isAbsolute() ) { |
| if( configDir != null ) |
| base=new File( configDir, base.getPath()); |
| else |
| base=new File( base.getAbsolutePath()); //?? |
| } |
| File parent=new File(base.getParent()); |
| if(!parent.exists()){ |
| if(!parent.mkdirs()){ |
| throw new RuntimeException( |
| "Unable to create path to config file :"+ |
| base.getAbsolutePath()); |
| } |
| } |
| return base; |
| } |
| |
| public static String patch(String path) { |
| String patchPath = path; |
| |
| // Move drive spec to the front of the path |
| if (patchPath.length() >= 3 && |
| patchPath.charAt(0) == '/' && |
| Character.isLetter(patchPath.charAt(1)) && |
| patchPath.charAt(2) == ':') { |
| patchPath=patchPath.substring(1,3)+"/"+patchPath.substring(3); |
| } |
| |
| // Eliminate consecutive slashes after the drive spec |
| if (patchPath.length() >= 2 && |
| Character.isLetter(patchPath.charAt(0)) && |
| patchPath.charAt(1) == ':') { |
| char[] ca = patchPath.replace('/', '\\').toCharArray(); |
| char c; |
| StringBuffer sb = new StringBuffer(); |
| |
| for (int i = 0; i < ca.length; i++) { |
| if ((ca[i] != '\\') || |
| (ca[i] == '\\' && |
| i > 0 && |
| ca[i - 1] != '\\')) { |
| if (i == 0 && |
| Character.isLetter(ca[i]) && |
| i < ca.length - 1 && |
| ca[i + 1] == ':') { |
| c = Character.toUpperCase(ca[i]); |
| } else { |
| c = ca[i]; |
| } |
| |
| sb.append(c); |
| } |
| } |
| |
| patchPath = sb.toString(); |
| } |
| |
| // fix path on NetWare - all '/' become '\\' and remove duplicate '\\' |
| if (System.getProperty("os.name").startsWith("NetWare") && |
| path.length() >=3 && |
| path.indexOf(':') > 0) { |
| char[] ca = patchPath.replace('/', '\\').toCharArray(); |
| StringBuffer sb = new StringBuffer(); |
| |
| for (int i = 0; i < ca.length; i++) { |
| if ((ca[i] != '\\') || |
| (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) { |
| sb.append(ca[i]); |
| } |
| } |
| patchPath = sb.toString(); |
| } |
| |
| return patchPath; |
| } |
| |
| public static boolean isAbsolute( String path ) { |
| // normal file |
| if( path.startsWith("/" ) ) return true; |
| |
| if( path.startsWith(File.separator ) ) return true; |
| |
| // win c: |
| if (path.length() >= 3 && |
| Character.isLetter(path.charAt(0)) && |
| path.charAt(1) == ':') |
| return true; |
| |
| // NetWare volume: |
| if (System.getProperty("os.name").startsWith("NetWare") && |
| path.length() >=3 && |
| path.indexOf(':') > 0) |
| return true; |
| |
| return false; |
| } |
| } |