• Icon: Bug Bug
    • Resolution: Fixed
    • Icon: Blocker Blocker
    • _unsorted
    • None
    • Platform: All, OS: All

      v1.271 changes the security filter implementation in hudson. Unfortunately this
      causes NPE to be thrown at application start in WebSphere with the following
      stack trace:

      Failed to initialize Hudson
      java.lang.NullPointerException
      at hudson.model.Hudson.setSecurityRealm(Hudson.java:1333)
      at hudson.model.Hudson.load(Hudson.java:1696)
      at hudson.model.Hudson.<init>(Hudson.java:446)

      at hudson.WebAppMain$2.run(WebAppMain.java:160)

      The NPE is caused because the instance of HudsonFilter is not available at the
      initializing time of Hudson instance in the servlet context. This is probably
      caused by the fact that WebSphere loads and initializes Filters at the first
      call to the filter mapped resources and not at the start-up of the application.
      The related code segments are:

      WebAppMain tries to init Hudson in separate thread:

      new Thread("hudson initialization thread") {
      public void run() {
      try {
      try

      { context.setAttribute(APP,new Hudson(home,context)); }

      catch( IOException e )

      { throw new Error(e); }

      initing new Hudson calls load(), which in turn tries to set the security realm
      to the proxy filter (in hudson.model.Hudson):

      public void setSecurityRealm(SecurityRealm securityRealm) {
      if(securityRealm==null)
      securityRealm= SecurityRealm.NO_AUTHENTICATION;
      this.securityRealm = securityRealm;
      // reset the filters and proxies for the new SecurityRealm
      try

      { HudsonFilter.get(servletContext).reset(securityRealm); }

      catch (ServletException e) {
      // for binary compatibility, this method cannot throw a checked
      exception
      throw new AcegiSecurityException("Failed to configure filter",e) {};
      }
      }

      HudsonFilter.get(context) tries to find its own instance saved at the init time
      in its init(FilterConfig) method into the servlet context and fails because the
      filter hasn't been initialized yet (in hudson.security.HudsonFilter):

      public void init(FilterConfig filterConfig) throws ServletException

      { this.filterConfig = filterConfig; // this is how we make us available to the rest of Hudson. filterConfig.getServletContext().setAttribute(HudsonFilter.class.getName(),this); }

      /**

      • Gets the {@link HudsonFilter}

        created for the given

        {@link ServletContext}

        .
        */
        public static HudsonFilter get(ServletContext context)

        { return (HudsonFilter)context.getAttribute(HudsonFilter.class.getName()); }

      IMHO the implementation here relays upon the availability of the servlet filters
      at application start-up time, but this is container specific implementation,
      because the servlet specification does not define exactly at what time the
      container should load and initialize a filter, but only that this must be done
      prior to first call to mapped resource for this filter. Because of this I can't
      consider this as a WebSphere incompatibility.

          [JENKINS-3069] NPE in WebSphere at start-up since v1.271

          Romain Seguy added a comment -

          I agree, this is not a WAS incompatibility, this is more because the code
          assumes that Filters are loaded before the ServletContextListeners (WebAppMain
          in our case).
          I propose to cure this issue by doing the following:

          • Set a static SECURITY_NOT_INITIALIZED var in Hudson to false
          • Handle the NPE in Hudson.setSecurityRealm() + issue an info message to warn
            that the security can't be set up + set SECURITY_NOT_INITIALIZED to true
          • Update Hudson.init() so that it checks for the value of
            SECURITY_NOT_INITIALIZED. The filter would then, if required, setup the security
            (which is a simple call to reset()) during init()

          Here is the patch:
          Index: main/core/src/main/java/hudson/model/Hudson.java
          ===================================================================
          — main/core/src/main/java/hudson/model/Hudson.java (revision 15536)
          +++ main/core/src/main/java/hudson/model/Hudson.java (working copy)
          @@ -1424,7 +1424,18 @@
          this.securityRealm = securityRealm;
          // reset the filters and proxies for the new SecurityRealm
          try {

          • HudsonFilter.get(servletContext).reset(securityRealm);
            + HudsonFilter filter = HudsonFilter.get(servletContext);
            + if(filter == null) { + // Fix for #3069: This filter is not necessarily initialized before the servlets; + // We set the security flag to true so that HudsonFilter knows it has to setup + // the security + LOGGER.log(Level.INFO, "HudsonFilter has not yet been initialized: Can't perform security setup for now"); + SECURITY_NOT_INITIALIZED = true; + }

            + else

            { + LOGGER.log(Level.INFO, "HudsonFilter has been previously initialized; Setting security up"); + filter.reset(securityRealm); + LOGGER.log(Level.INFO, "Security is now fully set up"); + }

            } catch (ServletException e) {
            // for binary compatibility, this method cannot throw a checked
            exception
            throw new AcegiSecurityException("Failed to configure filter",e) {};
            @@ -1788,6 +1799,12 @@
            else
            authorizationStrategy = new LegacyAuthorizationStrategy();
            }
            if(securityRealm==null) {
            if(useSecurity==null || !useSecurity)
            setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
            @@ -3106,6 +3123,9 @@
            public static boolean PARALLEL_LOAD =
            Boolean.getBoolean(Hudson.class.getName()+".parallelLoad");
            public static boolean KILL_AFTER_LOAD =
            Boolean.getBoolean(Hudson.class.getName()+".killAfterLoad");
            public static boolean LOG_STARTUP_PERFORMANCE =
            Boolean.getBoolean(Hudson.class.getName()+".logStartupPerformance");
            + // Fix for #3069: SECURITY_NOT_INITIALIZED will become true if the filters
            are not initialized before the servlets
            + // (this is the case on WebSphere) so that HudsonFilter knows it has to set
            the security up
            + public static boolean SECURITY_NOT_INITIALIZED = false;

          private static final Logger LOGGER = Logger.getLogger(Hudson.class.getName());

          Index: main/core/src/main/java/hudson/security/HudsonFilter.java
          ===================================================================
          — main/core/src/main/java/hudson/security/HudsonFilter.java (revision 15536)
          +++ main/core/src/main/java/hudson/security/HudsonFilter.java (working copy)
          @@ -23,9 +23,20 @@
          */
          package hudson.security;

          -import javax.servlet.*;
          +import hudson.model.Hudson;
          +
          import java.io.IOException;
          +import java.util.logging.Level;
          +import java.util.logging.Logger;

          +import javax.servlet.Filter;
          +import javax.servlet.FilterChain;
          +import javax.servlet.FilterConfig;
          +import javax.servlet.ServletContext;
          +import javax.servlet.ServletException;
          +import javax.servlet.ServletRequest;
          +import javax.servlet.ServletResponse;
          +
          import org.acegisecurity.AuthenticationManager;
          import org.acegisecurity.ui.rememberme.RememberMeServices;
          import org.acegisecurity.userdetails.UserDetailsService;
          @@ -42,7 +53,11 @@

          • @since 1.160
            */
            public class HudsonFilter implements Filter {
          • /**
            + private static final Logger LOGGER =
            Logger.getLogger(HudsonFilter.class.getName());
            +
            + /**
          • The SecurityRealm specific filter.
            */
            private volatile Filter filter;
            @@ -88,6 +103,18 @@
            this.filterConfig = filterConfig;
            // this is how we make us available to the rest of Hudson.

          filterConfig.getServletContext().setAttribute(HudsonFilter.class.getName(),this);
          + if(Hudson.SECURITY_NOT_INITIALIZED)

          { + // Fix for #3069: This filter is not necessarily initialized before the servlets; + // If it's the case, we need to initialize it before anything else is processed + LOGGER.log(Level.INFO, "Security is not yet set up; Initializing it..."); + Hudson.SECURITY_NOT_INITIALIZED = true; + SecurityRealm securityRealm = Hudson.getInstance().getSecurityRealm(); + reset(securityRealm); + LOGGER.log(Level.INFO, "Security is now fully set up"); + }

          }

          /**
          @@ -123,6 +150,9 @@
          }

          public void doFilter(ServletRequest request, ServletResponse response,
          FilterChain chain) throws IOException, ServletException {
          // to deal with concurrency, we need to capture the object.
          Filter f = filter;

          Romain Seguy added a comment - I agree, this is not a WAS incompatibility, this is more because the code assumes that Filters are loaded before the ServletContextListeners (WebAppMain in our case). I propose to cure this issue by doing the following: Set a static SECURITY_NOT_INITIALIZED var in Hudson to false Handle the NPE in Hudson.setSecurityRealm() + issue an info message to warn that the security can't be set up + set SECURITY_NOT_INITIALIZED to true Update Hudson.init() so that it checks for the value of SECURITY_NOT_INITIALIZED. The filter would then, if required, setup the security (which is a simple call to reset()) during init() Here is the patch: Index: main/core/src/main/java/hudson/model/Hudson.java =================================================================== — main/core/src/main/java/hudson/model/Hudson.java (revision 15536) +++ main/core/src/main/java/hudson/model/Hudson.java (working copy) @@ -1424,7 +1424,18 @@ this.securityRealm = securityRealm; // reset the filters and proxies for the new SecurityRealm try { HudsonFilter.get(servletContext).reset(securityRealm); + HudsonFilter filter = HudsonFilter.get(servletContext); + if(filter == null) { + // Fix for #3069: This filter is not necessarily initialized before the servlets; + // We set the security flag to true so that HudsonFilter knows it has to setup + // the security + LOGGER.log(Level.INFO, "HudsonFilter has not yet been initialized: Can't perform security setup for now"); + SECURITY_NOT_INITIALIZED = true; + } + else { + LOGGER.log(Level.INFO, "HudsonFilter has been previously initialized; Setting security up"); + filter.reset(securityRealm); + LOGGER.log(Level.INFO, "Security is now fully set up"); + } } catch (ServletException e) { // for binary compatibility, this method cannot throw a checked exception throw new AcegiSecurityException("Failed to configure filter",e) {}; @@ -1788,6 +1799,12 @@ else authorizationStrategy = new LegacyAuthorizationStrategy(); } if(securityRealm==null) { if(useSecurity==null || !useSecurity) setSecurityRealm(SecurityRealm.NO_AUTHENTICATION); @@ -3106,6 +3123,9 @@ public static boolean PARALLEL_LOAD = Boolean.getBoolean(Hudson.class.getName()+".parallelLoad"); public static boolean KILL_AFTER_LOAD = Boolean.getBoolean(Hudson.class.getName()+".killAfterLoad"); public static boolean LOG_STARTUP_PERFORMANCE = Boolean.getBoolean(Hudson.class.getName()+".logStartupPerformance"); + // Fix for #3069: SECURITY_NOT_INITIALIZED will become true if the filters are not initialized before the servlets + // (this is the case on WebSphere) so that HudsonFilter knows it has to set the security up + public static boolean SECURITY_NOT_INITIALIZED = false; private static final Logger LOGGER = Logger.getLogger(Hudson.class.getName()); Index: main/core/src/main/java/hudson/security/HudsonFilter.java =================================================================== — main/core/src/main/java/hudson/security/HudsonFilter.java (revision 15536) +++ main/core/src/main/java/hudson/security/HudsonFilter.java (working copy) @@ -23,9 +23,20 @@ */ package hudson.security; -import javax.servlet.*; +import hudson.model.Hudson; + import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + import org.acegisecurity.AuthenticationManager; import org.acegisecurity.ui.rememberme.RememberMeServices; import org.acegisecurity.userdetails.UserDetailsService; @@ -42,7 +53,11 @@ @since 1.160 */ public class HudsonFilter implements Filter { /** + private static final Logger LOGGER = Logger.getLogger(HudsonFilter.class.getName()); + + /** The SecurityRealm specific filter. */ private volatile Filter filter; @@ -88,6 +103,18 @@ this.filterConfig = filterConfig; // this is how we make us available to the rest of Hudson. filterConfig.getServletContext().setAttribute(HudsonFilter.class.getName(),this); + if(Hudson.SECURITY_NOT_INITIALIZED) { + // Fix for #3069: This filter is not necessarily initialized before the servlets; + // If it's the case, we need to initialize it before anything else is processed + LOGGER.log(Level.INFO, "Security is not yet set up; Initializing it..."); + Hudson.SECURITY_NOT_INITIALIZED = true; + SecurityRealm securityRealm = Hudson.getInstance().getSecurityRealm(); + reset(securityRealm); + LOGGER.log(Level.INFO, "Security is now fully set up"); + } } /** @@ -123,6 +150,9 @@ } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // to deal with concurrency, we need to capture the object. Filter f = filter;

          Romain Seguy added a comment -

          I've searched a little bit more on filters loading and the Javadoc for J2EE 1.4
          says this:
          "All ServletContextListeners are notified of context initialization before any
          filter or servlet in the web application is initialized."
          http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/ServletContextListener.html#contextInitialized(javax.servlet.ServletContextEvent)
          ==> As the current implementation requires that HudsonFilter gets loaded before
          Hudson.load() is run, it unfortunately doesn't go in the spec way

          Romain Seguy added a comment - I've searched a little bit more on filters loading and the Javadoc for J2EE 1.4 says this: "All ServletContextListeners are notified of context initialization before any filter or servlet in the web application is initialized." http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/ServletContextListener.html#contextInitialized(javax.servlet.ServletContextEvent) ==> As the current implementation requires that HudsonFilter gets loaded before Hudson.load() is run, it unfortunately doesn't go in the spec way

          ilkomiliev added a comment -

          I see - do we really need a filter here - just a thought... Nevertheless, I'd
          like to try your patch as a workaround at the moment,but I have troubles
          applying it from browser. can you please send it to me per email

          ilkomiliev added a comment - I see - do we really need a filter here - just a thought... Nevertheless, I'd like to try your patch as a workaround at the moment,but I have troubles applying it from browser. can you please send it to me per email

          ilkomiliev added a comment -

          Thanks for the patch in the zip file - I've tried hudson 1.285 pre-patched with
          it and everything works ok here, so I'd suggest that this is commited to have a
          working version for WebSphere.

          ilkomiliev added a comment - Thanks for the patch in the zip file - I've tried hudson 1.285 pre-patched with it and everything works ok here, so I'd suggest that this is commited to have a working version for WebSphere.

          Romain Seguy added a comment -

          May someone take a look at this and commit the fix please?

          Regards.

          Romain Seguy added a comment - May someone take a look at this and commit the fix please? Regards.

          ilkomiliev added a comment -

          Can someone please take the responsibility to commit this changes please! It has been a week since
          this is pending here. Thanks!

          ilkomiliev added a comment - Can someone please take the responsibility to commit this changes please! It has been a week since this is pending here. Thanks!

          Romain Seguy added a comment -

          Created an attachment (id=587)
          Patch updated as of 03/03/2009 to fit the latest Hudson version

          Romain Seguy added a comment - Created an attachment (id=587) Patch updated as of 03/03/2009 to fit the latest Hudson version

          evantd added a comment -

          I'm seeing this with Jetty as well, and the attached patch fixes it for me.

          evantd added a comment - I'm seeing this with Jetty as well, and the attached patch fixes it for me.

          Code changed in hudson
          User: : kohsuke
          Path:
          trunk/hudson/main/core/src/main/java/hudson/model/Descriptor.java
          trunk/hudson/main/core/src/main/java/hudson/model/Hudson.java
          trunk/hudson/main/core/src/main/java/hudson/security/ChainedServletFilter.java
          trunk/hudson/main/core/src/main/java/hudson/security/HudsonFilter.java
          trunk/hudson/main/core/src/main/java/hudson/security/SecurityRealm.java
          trunk/www/changelog.html
          http://fisheye4.cenqua.com/changelog/hudson/?cs=16150
          Log:
          [FIXED JENKINS-3069]
          Fixed the initialization order issue.

          SCM/JIRA link daemon added a comment - Code changed in hudson User: : kohsuke Path: trunk/hudson/main/core/src/main/java/hudson/model/Descriptor.java trunk/hudson/main/core/src/main/java/hudson/model/Hudson.java trunk/hudson/main/core/src/main/java/hudson/security/ChainedServletFilter.java trunk/hudson/main/core/src/main/java/hudson/security/HudsonFilter.java trunk/hudson/main/core/src/main/java/hudson/security/SecurityRealm.java trunk/www/changelog.html http://fisheye4.cenqua.com/changelog/hudson/?cs=16150 Log: [FIXED JENKINS-3069] Fixed the initialization order issue.

          Romain Seguy added a comment -

          Verified with 1.191 on WAS 7.0.0.1 ND. Works fine. Thanks.

          Romain Seguy added a comment - Verified with 1.191 on WAS 7.0.0.1 ND. Works fine. Thanks.

            Unassigned Unassigned
            ilkomiliev ilkomiliev
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: