You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ho...@apache.org on 2004/05/15 17:41:12 UTC

cvs commit: logging-log4j-sandbox/src/java/org/apache/log4j/servlet InitShutdownController.java

hoju        2004/05/15 08:41:12

  Modified:    src/java/org/apache/log4j/servlet
                        InitShutdownController.java
  Log:
  Improvements suggested by Andreas Werner (modularizing the methods and a few bug fixes), plus I now take care to modify the thread context class loader during initialization to avoid class cast exceptions based on the following article...
  http://www-106.ibm.com/developerworks/websphere/library/techarticles/0310_searle/searle.html
  
  Note that this class, currently, is meant to work with Log4j-1.2.8.  The current CVS of Log4j-1.3.0 is not compatible.  That should change in a future version.
  
  Jake
  
  Revision  Changes    Path
  1.4       +254 -168  logging-log4j-sandbox/src/java/org/apache/log4j/servlet/InitShutdownController.java
  
  Index: InitShutdownController.java
  ===================================================================
  RCS file: /home/cvs/logging-log4j-sandbox/src/java/org/apache/log4j/servlet/InitShutdownController.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- InitShutdownController.java	28 Feb 2004 13:55:05 -0000	1.3
  +++ InitShutdownController.java	15 May 2004 15:41:12 -0000	1.4
  @@ -39,7 +39,9 @@
    * and {@link InitServlet}, this class can be used by any class at runtime to
    * control initialization and shutdown of Log4j loggers and appenders.</p>
    *
  + * @see http://nagoya.apache.org/wiki/apachewiki.cgi?Log4JProjectPages/AppContainerLogging
    * @author <a href="mailto:hoju@visi.com">Jacob Kjome</a>
  + * @author Andreas Werner
    * @since  1.3
    */
   public class InitShutdownController {
  @@ -107,155 +109,237 @@
      * @param context the current servlet context
      */
     public static void initializeLog4j(final ServletContext context) {
  +    ClassLoader savedClassLoader = null;
  +
  +    try {
  +      savedClassLoader = Thread.currentThread().getContextClassLoader();
  +
  +      //attempt to set the thread context class loader to the current class
  +      //loader to avoid ClassCastExceptions when this library is in a parent
  +      //class loader (along with log4j.jar) and a webapp includes log4j.jar
  +      //in WEB-INF/lib, but not this library (assuming they aren't both in
  +      //log4j.jar).
  +      //based on the following article...
  +      //http://www-106.ibm.com/developerworks/websphere/library/techarticles/0310_searle/searle.html
  +      Thread.currentThread().setContextClassLoader(
  +        InitShutdownController.class.getClassLoader());
  +    } catch (SecurityException se) {
  +      //oh, well, we tried.  Security is turned on and we aren't allowed to
  +      //tamper with the thread context class loader.  You're on your own!
  +    }
  +
  +    try {
  +      //Attempt to set the repository selector no matter what else happens
  +      setSelector(context);
  +
  +      String configPath = getLog4jConfigPathFromContext(context);
  +
  +      if ((configPath != null) && (configPath.length() >= 1)) {
  +        setLog4jLogHome(context);
  +
  +        if (context.getRealPath("/") != null) {
  +          configureLog4jFromFile(configPath, context);
  +        } else {
  +          configureLog4jFromURL(configPath, context);
  +        }
  +      } else {
  +        if (configPath == null) {
  +          LogLog.error("Missing log4j-config servlet parameter missing.");
  +        } else {
  +          LogLog.error("Zero length Log4j config file path given.");
  +        }
  +
  +        displayConfigNotFoundMessage();
  +      }
  +    } finally {
  +      if (savedClassLoader != null) {
  +        //reset the thread context class loader to the original saved class
  +        //loader now that our purpose (avoiding class cast exceptions) has been
  +        //served
  +        Thread.currentThread().setContextClassLoader(savedClassLoader);
  +      }
  +    }
  +  }
  +
  +  /**
  +   * Determines the path to log4j config file from the servlet context param
  +   * 'logj4-config'.  The path is assumed to be relative to the current context.
  +   * If set, the path is normalized with any preceeding slash removed as the
  +   * other methods expect the path to be formatted this way.
  +   *
  +   * @param context The servlet context
  +   * @return the path to the log4j config file within the context or null if
  +   *         the 'log4j-config' context param is not set.
  +   */
  +  private static String getLog4jConfigPathFromContext(
  +    final ServletContext context) {
       String configPath = context.getInitParameter(PARAM_LOG4J_CONFIG_PATH);
   
  -    // if the log4j-config parameter is not set, then no point in trying
       if (configPath != null) {
         if (configPath.startsWith("/")) {
           configPath = (configPath.length() > 1) ? configPath.substring(1) : "";
         }
  +    }
   
  -      // if the configPath is an empty string, then no point in trying
  -      if (configPath.length() >= 1) {
  -        // set up log path System property
  -        String logHome = context.getInitParameter(PARAM_LOG4J_LOG_HOME);
  -
  -        if (logHome != null) {
  -          // set up custom log path system property
  -          setFileAppenderSystemProperty(logHome, context);
  -        }
  +    return configPath;
  +  }
   
  -        boolean isXMLConfigFile = (configPath.endsWith(".xml")) ? true : false;
  -        String contextPath = context.getRealPath("/");
  +  /**
  +   * Determines the log4j home to use. If the context parameter
  +   * 'log4j-log-home' is set, this is used as the environment variable.
  +   * Otherwise the the default 'WEB-INF/logs' is used.
  +   *
  +   * @param context the current servlet context
  +   */
  +  private static void setLog4jLogHome(final ServletContext context) {
  +    // set up log path System property
  +    String logHome = context.getInitParameter(PARAM_LOG4J_LOG_HOME);
  +
  +    if (logHome != null) {
  +      // set up custom log path system property
  +      setFileAppenderSystemProperty(logHome, context);
  +    } else {
  +      String contextPath = context.getRealPath("/");
   
  -        if (contextPath != null) {
  -          // The webapp is deployed directly off the filesystem,
  -          // not from a .war file so we *can* do File IO.
  -          // This means we can use configureAndWatch() to re-read
  -          // the the config file at defined intervals.
  -          // Now let's check if the given configPath actually exists.
  -          if (logHome == null) {
  -            // no log path specified in web.xml. Setting to default
  -            logHome = contextPath + DEFAULT_LOG_HOME;
  -            setFileAppenderSystemProperty(logHome, context);
  -          }
  -
  -          String systemConfigPath =
  -            configPath.replace('/', File.separatorChar);
  -          File log4jFile = new File(contextPath + systemConfigPath);
  -
  -          if (log4jFile.canRead()) {
  -            log4jFile = null;
  -
  -            String timerInterval =
  -              context.getInitParameter(PARAM_LOG4J_WATCH_INTERVAL);
  -            long timerIntervalVal = 0L;
  -
  -            if (timerInterval != null) {
  -              try {
  -                timerIntervalVal = Integer.valueOf(timerInterval).longValue();
  -              } catch (NumberFormatException nfe) {
  -                //ignore...we just won't use configureAndWatch if there is no
  -                //valid int
  -                ;
  -              }
  -            }
  -
  -            setSelector(context);
  -            context.log(
  -              "Configuring Log4j from File: " + contextPath + systemConfigPath);
  -
  -            if (timerIntervalVal > 0) {
  -              context.log(
  -                "Configuring Log4j with watch interval: " + timerIntervalVal
  -                + "ms");
  -
  -              if (isXMLConfigFile) {
  -                DOMConfigurator.configureAndWatch(
  -                  contextPath + systemConfigPath, timerIntervalVal);
  -              } else {
  -                PropertyConfigurator.configureAndWatch(
  -                  contextPath + systemConfigPath, timerIntervalVal);
  -              }
  -            } else {
  -              if (isXMLConfigFile) {
  -                DOMConfigurator.configure(contextPath + systemConfigPath);
  -              } else {
  -                PropertyConfigurator.configure(contextPath + systemConfigPath);
  -              }
  -            }
  -          } else {
  -            //The given configPath does not exist.  So, let's just let Log4j
  -            //look for the default files (log4j.properties or log4j.xml) on
  -            //its own.
  -            displayConfigNotFoundMessage();
  -          }
  +      if (contextPath != null) {
  +        //no log path specified in web.xml. Setting to default within current
  +        //context path
  +        logHome = contextPath + DEFAULT_LOG_HOME;
  +        setFileAppenderSystemProperty(logHome, context);
  +      }
  +    }
  +  }
   
  -          //end log4jFile.canRead() check
  -        } else {
  -          //The webapp is deployed from a .war file, not directly
  -          //off the file system so we *cannot* do File IO.
  -          //Note that we *won't* be able to use configureAndWatch() here
  -          //because that requires an absolute system file path.
  -          //Now let's check if the given configPath actually exists.
  -          URL log4jURL = null;
  -
  -          try {
  -            log4jURL = context.getResource("/" + configPath);
  -          } catch (MalformedURLException murle) {
  -            //ignore...we check for null later
  -            ;
  -          }
  -
  -          if (log4jURL != null) {
  -            setSelector(context);
  -            context.log("Configuring Log4j from URL at path: /" + configPath);
  -
  -            if (isXMLConfigFile) {
  -              try {
  -                DOMConfigurator.configure(log4jURL);
  -
  -                //catch (javax.xml.parsers.FactoryConfigurationError fce) {}
  -              } catch (Exception e) {
  -                //report errors to server logs
  -                LogLog.error(e.getMessage());
  -              }
  -            } else {
  -              Properties log4jProps = new Properties();
  -
  -              try {
  -                log4jProps.load(log4jURL.openStream());
  -                PropertyConfigurator.configure(log4jProps);
  -
  -                //catch (java.io.IOException ioe) {}
  -              } catch (Exception e) {
  -                //report errors to server logs
  -                LogLog.error(e.getMessage());
  -              }
  -            }
  -          } else {
  -            //The given configPath does not exist.  So, let's just let Log4j
  -            //look for the default files (log4j.properties or log4j.xml) on
  -            //its own.
  -            displayConfigNotFoundMessage();
  -          }
  +  /**
  +   * Configures log4j via url
  +   *
  +   * @param configPath the location of the log4j configuration file
  +   *        relative to the context path
  +   * @param context the current servlet context
  +   */
  +  private static void configureLog4jFromURL(
  +    final String configPath, final ServletContext context) {
  +    //The webapp is deployed from a .war file, not directly off the file system
  +    //so we *cannot* do File IO. Note that we *won't* be able to use
  +    //configureAndWatch() here because that requires an absolute system file
  +    //path.
  +    boolean isXMLConfigFile = (configPath.endsWith(".xml")) ? true : false;
  +
  +    URL log4jURL = null;
  +
  +    try {
  +      log4jURL = context.getResource("/" + configPath);
  +    } catch (MalformedURLException murle) {
  +      //ignore...we check for null later
  +      ;
  +    }
   
  -          //end log4jURL null check
  -        }
  +    //Now let's check if the given configPath actually exists.
  +    if (log4jURL != null) {
  +      context.log("Configuring Log4j from URL at path: /" + configPath);
  +
  +      if (isXMLConfigFile) {
  +        try {
  +          DOMConfigurator.configure(log4jURL);
   
  -        //end contextPath null check
  +          //catch (javax.xml.parsers.FactoryConfigurationError fce) {}
  +        } catch (Exception e) {
  +          //report errors to server logs
  +          LogLog.error(e.getMessage());
  +        }
         } else {
  -        LogLog.error("Zero length Log4j config file path given.");
  -        displayConfigNotFoundMessage();
  +        Properties log4jProps = new Properties();
  +
  +        try {
  +          log4jProps.load(log4jURL.openStream());
  +          PropertyConfigurator.configure(log4jProps);
  +
  +          //catch (java.io.IOException ioe) {}
  +        } catch (Exception e) {
  +          //report errors to server logs
  +          LogLog.error(e.getMessage());
  +        }
         }
  +    } else {
  +      //The given configPath does not exist. So, let's just let Log4j look for
  +      //the default files (log4j.properties or log4j.xml) on its own.
  +      displayConfigNotFoundMessage();
  +    }
  +  }
  +
  +  /**
  +   * Configures log4j from a File
  +   *
  +   * @param systemConfigPath the fully qualified system path location of the
  +   *        log4j configuration file
  +   * @param context the current servlet context
  +   */
  +  private static void configureLog4jFromFile(
  +    final String configPath, final ServletContext context) {
  +    //The webapp is deployed directly off the filesystem, not from a .war file
  +    //so we *can* do File IO. This means we can use configureAndWatch() to
  +    //re-read the the config file at defined intervals.
  +    boolean isXMLConfigFile = (configPath.endsWith(".xml")) ? true : false;
  +
  +    String contextPath = context.getRealPath("/");
  +    String systemConfigPath = configPath.replace('/', File.separatorChar);
  +    File log4jFile = new File(contextPath + systemConfigPath);
  +
  +    //Now let's check if the given configPath actually exists.
  +    if (log4jFile.canRead()) {
  +      log4jFile = null;
   
  -      //end configPath length check
  +      long timerIntervalVal = getTimerIntervalFromContext(context);
  +      context.log(
  +        "Configuring Log4j from File: " + contextPath + systemConfigPath);
  +
  +      if (timerIntervalVal > 0) {
  +        context.log(
  +          "Configuring Log4j with watch interval: " + timerIntervalVal + "ms");
  +
  +        if (isXMLConfigFile) {
  +          DOMConfigurator.configureAndWatch(
  +            contextPath + systemConfigPath, timerIntervalVal);
  +        } else {
  +          PropertyConfigurator.configureAndWatch(
  +            contextPath + systemConfigPath, timerIntervalVal);
  +        }
  +      } else {
  +        if (isXMLConfigFile) {
  +          DOMConfigurator.configure(contextPath + systemConfigPath);
  +        } else {
  +          PropertyConfigurator.configure(contextPath + systemConfigPath);
  +        }
  +      }
       } else {
  -      LogLog.error("Missing log4j-config servlet parameter missing.");
  +      // The given configPath does not exist. So, let's just
  +      // let Log4j look for the default files (
  +      // log4j.properties or log4j.xml) on its own.
         displayConfigNotFoundMessage();
       }
  +  }
   
  -    //end configPath null check
  +  /**
  +   * Retrieves the timer interval from the servlet context.
  +   *
  +   * @param context the current servlet context
  +   */
  +  private static long getTimerIntervalFromContext(
  +    final ServletContext context) {
  +    String timerInterval =
  +      context.getInitParameter(PARAM_LOG4J_WATCH_INTERVAL);
  +    long timerIntervalVal = 0L;
  +
  +    if (timerInterval != null) {
  +      try {
  +        timerIntervalVal = Integer.valueOf(timerInterval).longValue();
  +      } catch (NumberFormatException nfe) {
  +        //ignore...we just won't use configureAndWatch if there is no valid int
  +        ;
  +      }
  +    }
  +
  +    return timerIntervalVal;
     }
   
     /**
  @@ -277,52 +361,54 @@
       final String logHome, final ServletContext context) {
       String logHomePropName = null;
       String customPropName = context.getInitParameter(PARAM_LOG4J_SYSPROP_NAME);
  +    File logHomeDir = new File(logHome);
   
  -    if (customPropName != null) {
  -      logHomePropName = customPropName;
  -    } else {
  -      File logHomeDir = new File(logHome);
  -
  -      if (logHomeDir.exists() || logHomeDir.mkdirs()) {
  -        /*String tempdir =
  -          "" + context.getAttribute("javax.servlet.context.tempdir");
  -        int lastSlash = tempdir.lastIndexOf(File.separator);
  -
  -        if ((tempdir.length() - 1) > lastSlash) {
  -          logHomePropName = tempdir.substring(lastSlash + 1) + ".log.home";
  -        }*/
  -        String contextPath = "";
  -
  -        try {
  -          //use a more standard way to obtain the context path name
  -          //which should work across all servers.  The tmpdir technique
  -          //(above) depends upon the naming scheme that Tomcat uses.
  -          String path = context.getResource("/").getPath();
  -
  -          //first remove trailing slash, then take what's left over
  -          //which should be the context path less the preceeding
  -          //slash such as "MyContext"
  -          contextPath = path.substring(0, path.lastIndexOf("/"));
  -          contextPath =
  -            contextPath.substring(contextPath.lastIndexOf("/") + 1);
  -        } catch (Exception e) {
  -          ;
  -        }
  -
  -        logHomePropName = contextPath + ".log.home";
  +    if (logHomeDir.exists() || logHomeDir.mkdirs()) {
  +      if (customPropName != null) {
  +        logHomePropName = customPropName;
  +      } else {
  +        logHomePropName = getContextPath(context) + ".log.home";
         }
  -    }
   
  -    if (logHomePropName != null) {
         context.log(
           "Setting system property [ " + logHomePropName + " ] to [ " + logHome
           + " ]");
         System.setProperty(logHomePropName, logHome);
  -    } else {
  -      context.log(
  -        "Unable to derive log4j system property name. Consider setting the "
  -        + "\"log4j-sysprop-name\" context parameter. No system property set.");
       }
  +  }
  +
  +  /**
  +   * Retrieves the context path of the web application from the servlet context.
  +   *
  +   * @param context the current servlet context
  +   * @return the derived context path, guaranteed non-null
  +   */
  +  private static String getContextPath(final ServletContext context) {
  +    //old way to determine context path
  +    //String tempdir = "" +
  +    //context.getAttribute("javax.servlet.context.tempdir");
  +    //int lastSlash = tempdir.lastIndexOf(File.separator);
  +    //if ((tempdir.length() - 1) > lastSlash) {
  +    //  logHomePropName = tempdir.substring(lastSlash + 1) + ".log.home";
  +    //}
  +    String contextPath = "";
  +
  +    try {
  +      //use a more standard way to obtain the context path name
  +      //which should work across all servers. The tmpdir technique
  +      //(above) depends upon the naming scheme that Tomcat uses.
  +      String path = context.getResource("/").getPath();
  +
  +      //first remove trailing slash, then take what's left over
  +      //which should be the context path less the preceeding
  +      //slash such as "MyContext"
  +      contextPath = path.substring(0, path.lastIndexOf("/"));
  +      contextPath = contextPath.substring(contextPath.lastIndexOf("/") + 1);
  +    } catch (Exception e) {
  +      ;
  +    }
  +
  +    return contextPath;
     }
   
     /**
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org