All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opentable.metrics.ready.MediocreReadyCheck Maven / Gradle / Ivy

/*
 * 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 com.opentable.metrics.ready;

import java.time.Instant;
import java.util.concurrent.atomic.AtomicReference;

import javax.inject.Named;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;

/**
 * This ready check does an OK job of reporting whether the application is ready.  It flips to being healthy when
 * the Spring application is all wired up (when the context is refreshed), and becomes unhealthy when closed.
 * There are some caveats—see the comments in the source.
 */
@Named("mediocreReadyCheck")
class MediocreReadyCheck extends ReadyCheck {
    private static final Logger LOG = LoggerFactory.getLogger(MediocreReadyCheck.class);

    /**
     * This bean is implicitly in singleton scope, so the healthy state will persist.
     */
    private final AtomicReference state = new AtomicReference<>(new State(false, null));

    @Override
    protected Result check() {
        return state.get().makeResult();
    }

    private void setHealthy(final boolean ready, final ApplicationContextEvent event) {
        LOG.info("setting mediocre ready {}; cause: {}", ready, event);
        state.set(new State(ready, event));
    }

    // Refreshed comes in as soon as everything is wired up, but closed doesn't have analogous sequencing.
    // Specifying an ordering doesn't help.  Specifying a shutdown hook still wouldn't enable us to reliably
    // get in ahead of other components.
    // TODO Improve.
    @EventListener
    public void refreshed(final ContextRefreshedEvent event) {
        setHealthy(true, event);
    }

    @EventListener
    public void closed(final ContextClosedEvent event) {
        setHealthy(false, event);
    }

    private static class State {
        private final boolean ready;
        private final ApplicationContextEvent cause;
        State(final boolean ready, final ApplicationContextEvent cause) {
            this.ready = ready;
            this.cause = cause;
        }
        private String formatMessage() {
            final String formatCause;
            if (cause == null) {
                formatCause = "initialized to unready";
            } else {
                formatCause = cause.getClass().getSimpleName() + " @ " + Instant.ofEpochMilli(cause.getTimestamp());
            }
            return formatCause;
        }
        private Result makeResult() {
            return ready ? Result.ready(formatMessage()) :
                    Result.ready(formatMessage());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy