/**
* Copyright 2014 Sunny Gleason and original author or authors
*
* 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.kazuki.v0.store.lifecycle;
import io.kazuki.v0.internal.helper.LogTranslation;
import io.kazuki.v0.store.management.ComponentDescriptor;
import io.kazuki.v0.store.management.ComponentRegistrar;
import io.kazuki.v0.store.management.KazukiComponent;
import io.kazuki.v0.store.management.impl.ComponentDescriptorImpl;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.inject.Inject;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableList;
/**
* The Lifecycle is a singleton instance (as hopefully enforced by the DI container) through which
* application lifecycle is controlled. This Lifecycle instance is used by a controller object in
* the app that invokes Lifecycle methods to inform components of application state changes.
*/
public class Lifecycle implements KazukiComponent<Lifecycle> {
private final Logger log = LogTranslation.getLogger(getClass());
private static final EnumSet<LifecycleEvent> reverseOrder = EnumSet.of(LifecycleEvent.UNANNOUNCE,
LifecycleEvent.STOP, LifecycleEvent.SHUTDOWN);
private final ConcurrentLinkedDeque<LifecycleAware> listeners = new ConcurrentLinkedDeque<>();
private final ConcurrentHashMap<LifecycleAware, LifecycleEvent> lastEvent =
new ConcurrentHashMap<>();
private final String name;
private final ComponentDescriptor<Lifecycle> componentDescriptor;
public Lifecycle(String name) {
this.name = name;
this.componentDescriptor =
new ComponentDescriptorImpl<Lifecycle>("KZ:Lifecycle:" + name, Lifecycle.class, this,
new ImmutableList.Builder().build());
}
/** Registers a listener with the lifecycle, thus 'subscribing' to events */
public void register(LifecycleAware listener) {
log.debug("Registering Lifecycle listener {}", listener);
listeners.add(listener);
}
@Override
public ComponentDescriptor<Lifecycle> getComponentDescriptor() {
return this.componentDescriptor;
}
@Override
@Inject
public void registerAsComponent(ComponentRegistrar manager) {
manager.register(this.componentDescriptor);
}
/**
* Init: Components should sanity check and prepare themselves for use.
*/
public void init() {
fireEvent(LifecycleEvent.INIT);
}
/**
* Start: service is listening but not taking requests.
*/
public void start() {
fireEvent(LifecycleEvent.START);
}
/**
* Announce: start taking requests.
*/
public void announce() {
fireEvent(LifecycleEvent.ANNOUNCE);
}
/**
* Unannounce: stop advertising, stop taking requests.
*/
public void unannounce() {
fireEvent(LifecycleEvent.UNANNOUNCE);
}
/**
* Shutdown: dispose resources in a quick and orderly fashion.
*/
public void shutdown() {
fireEvent(LifecycleEvent.SHUTDOWN);
}
/**
* Stop: halting of JVM is imminent.
*/
public void stop() {
fireEvent(LifecycleEvent.STOP);
}
public String getName() {
return name;
}
public Map<LifecycleAware, LifecycleEvent> getLastEvents() {
return Collections.unmodifiableMap(lastEvent);
}
/**
* Sends the event synchronously to each listener in order.
*/
private void fireEvent(LifecycleEvent event) {
log.debug("Firing lifecycle event {} to all listeners", event.name());
Iterator<LifecycleAware> iter =
reverseOrder.contains(event) ? listeners.descendingIterator() : listeners.iterator();
while (iter.hasNext()) {
LifecycleAware listener = iter.next();
log.trace("Firing lifecycle event {} to listener {}", event.name(), listener);
listener.eventFired(event);
lastEvent.put(listener, event);
}
log.debug("Fired lifecycle event {}", event.name());
}
}