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

io.airlift.bootstrap.LifeCycleManager Maven / Gradle / Ivy

There is a newer version: 297
Show newest version
/*
 * Copyright 2010 Proofpoint, Inc.
 *
 * 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 io.airlift.bootstrap;

import com.google.common.collect.Lists;
import io.airlift.log.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Manages PostConstruct and PreDestroy life cycles
 */
public final class LifeCycleManager
{
    private final Logger log = Logger.get(getClass());
    private final AtomicReference state = new AtomicReference<>(State.LATENT);
    private final Queue managedInstances = new ConcurrentLinkedQueue<>();
    private final LifeCycleMethodsMap methodsMap;

    private enum State
    {
        LATENT,
        STARTING,
        STARTED,
        STOPPING,
        STOPPED
    }

    /**
     * @param managedInstances list of objects that have life cycle annotations
     * @param methodsMap existing or new methods map
     * @throws Exception exceptions starting instances (depending on mode)
     */
    public LifeCycleManager(List managedInstances, LifeCycleMethodsMap methodsMap)
            throws Exception
    {
        this.methodsMap = (methodsMap != null) ? methodsMap : new LifeCycleMethodsMap();
        for (Object instance : managedInstances) {
            addInstance(instance);
        }
    }

    /**
     * Returns the number of managed instances
     *
     * @return qty
     */
    public int size()
    {
        return managedInstances.size();
    }

    /**
     * Start the life cycle - all instances will have their {@link javax.annotation.PostConstruct} method(s) called
     *
     * @throws Exception errors
     */
    public void start()
            throws Exception
    {
        if (!state.compareAndSet(State.LATENT, State.STARTING)) {
            throw new Exception("System already starting");
        }
        log.info("Life cycle starting...");

        for (Object obj : managedInstances) {
            LifeCycleMethods methods = methodsMap.get(obj.getClass());
            if (!methods.hasFor(PreDestroy.class)) {
                managedInstances.remove(obj);   // remove reference to instances that aren't needed anymore
            }
        }

        Runtime.getRuntime().addShutdownHook(new Thread()
        {
            @Override
            public void run()
            {
                try {
                    LifeCycleManager.this.stop();
                }
                catch (Exception e) {
                    log.error(e, "Trying to shut down");
                }
            }
        });

        state.set(State.STARTED);
        log.info("Life cycle startup complete. System ready.");
    }

    /**
     * Stop the life cycle - all instances will have their {@link javax.annotation.PreDestroy} method(s) called
     *
     * @throws Exception errors
     */
    public void stop()
            throws Exception
    {
        if (!state.compareAndSet(State.STARTED, State.STOPPING)) {
            return;
        }

        log.info("Life cycle stopping...");

        List reversedInstances = Lists.newArrayList(managedInstances);
        Collections.reverse(reversedInstances);

        for (Object obj : reversedInstances) {
            log.debug("Stopping %s", obj.getClass().getName());
            LifeCycleMethods methods = methodsMap.get(obj.getClass());
            for (Method preDestroy : methods.methodsFor(PreDestroy.class)) {
                log.debug("\t%s()", preDestroy.getName());
                preDestroy.invoke(obj);
            }
        }

        state.set(State.STOPPED);
        log.info("Life cycle stopped.");
    }

    /**
     * Add an additional managed instance
     *
     * @param instance instance to add
     * @throws Exception errors
     */
    public void addInstance(Object instance)
            throws Exception
    {
        State currentState = state.get();
        if ((currentState == State.STOPPING) || (currentState == State.STOPPED)) {
            throw new IllegalStateException();
        }
        else {
            startInstance(instance);
            if (methodsMap.get(instance.getClass()).hasFor(PreDestroy.class)) {
                managedInstances.add(instance);
            }
        }
    }

    private void startInstance(Object obj)
            throws IllegalAccessException, InvocationTargetException
    {
        log.debug("Starting %s", obj.getClass().getName());
        LifeCycleMethods methods = methodsMap.get(obj.getClass());
        for (Method postConstruct : methods.methodsFor(PostConstruct.class)) {
            log.debug("\t%s()", postConstruct.getName());
            postConstruct.invoke(obj);
        }
    }
}