/**
 * The MIT License
 *
 * Copyright (c) 2010-2011 Sonatype, Inc. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.hudsonci.events.ready;

import org.hudsonci.events.EventPublisher;

import hudson.init.InitMilestone;
import hudson.model.Hudson;
import hudson.security.HudsonFilter;
import hudson.stapler.WebAppController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.servlet.Filter;
import java.lang.reflect.Field;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Detects when the system is ready for work and publishes a {@link ReadyEvent}.
 *
 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
 * @since 2.1.0
 */
@Named
@Singleton
public class ReadyDetector
    extends Thread
{
    private static final Logger log = LoggerFactory.getLogger(ReadyDetector.class);

    private final EventPublisher publisher;

    private final Hudson hudson;

    private final WebAppController controller;

    @Inject
    public ReadyDetector(final EventPublisher publisher, final Hudson hudson) {
        this.publisher = checkNotNull(publisher);
        this.hudson = checkNotNull(hudson);
        this.controller = WebAppController.get();
        setDaemon(true);
    }

    public void run() {
        while (true) {
            if (isReady()) {
                publisher.publish(new ReadyEvent(hudson));
                break;
            }
            else {
                try {
                    Thread.sleep(500);
                }
                catch (InterruptedException e) {
                    log.warn("Interrupted while waiting for initialization; ignoring", e);
                }
            }
        }
    }

    private boolean isReady() {
        // Hudson does not give us a nice InitMilestone when its really ready,
        // but if its not yet COMPLETED, don't bother trying to figure out more
        if (hudson.getInitLevel() != InitMilestone.COMPLETED) {
            return false;
        }

        HudsonFilter filter = HudsonFilter.get(hudson.servletContext);
        if (filter == null) {
            return false;
        }

        // Need to get access to the filter's filter field to see if its actually initialized or not
        // it does not expose it directly, so we have to use reflection to force access

        Filter delegate = getDelegate(filter);
        if (delegate == null) {
            return false;
        }

        // At this point we _should_ be ready, see if the app root object is installed... fingers crossed!
        Object app = controller.current(); // FIXME: This may actually be the only check needed?
        return app instanceof hudson.model.Hudson;
    }

    private Filter getDelegate(final HudsonFilter filter) {
        assert filter != null;
        try {
            Field field = filter.getClass().getDeclaredField("filter");
            field.setAccessible(true);
            return (Filter) field.get(filter);
        }
        catch (Exception e) {
            throw new Error("Failed to access HudsonFilter.filter delegate", e);
        }
    }
}

@@ -3673,16 +3673,34 @@
 rossed!%0A
+        try %7B%0A    
         
@@ -3792,24 +3792,28 @@
 ed?%0A        
+    
 return app i
@@ -3843,16 +3843,125 @@
 Hudson;%0A
+        %7D catch (IllegalStateException e) %7B%0A            return false; // context not yet available%0A        %7D%0A
     %7D%0A%0A 

Handle cases where the servlet container is slow to start during testing