Use Provider to obtain LifecycleListeners
Instead of holding onto a singleton for a LifecycleListener, allow the listener to be created on demand using a Provider. Use explicit support of the HandlerRegistration type, enabling these to always be cleared before any LifecycleListener is stopped. This way registrations from plugins can always be cleared even if the list of listeners to start didn't finish running to the end. During stop only run those listeners that attempted to start. We assume a listener is prepared to run its stop method as soon as its start method begins execution. This means a start method that can fail must use guards in its stop method to handle stopping after a partial start failure. Change-Id: I86ae39a51e3b7fad5c5af445bf7c70133222c31a
This commit is contained in:
@@ -14,75 +14,89 @@
|
|||||||
|
|
||||||
package com.google.gerrit.lifecycle;
|
package com.google.gerrit.lifecycle;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.gerrit.extensions.registration.RegistrationHandle;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.util.Providers;
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Tracks and executes registered {@link LifecycleListener}s. */
|
/** Tracks and executes registered {@link LifecycleListener}s. */
|
||||||
public class LifecycleManager {
|
public class LifecycleManager {
|
||||||
private final LinkedHashMap<LifecycleListener, Boolean> listeners =
|
private final List<Provider<LifecycleListener>> listeners = newList();
|
||||||
new LinkedHashMap<LifecycleListener, Boolean>();
|
private final List<RegistrationHandle> handles = newList();
|
||||||
|
|
||||||
private boolean started;
|
/** Index of the last listener to start successfully; -1 when not started. */
|
||||||
|
private int startedIndex = -1;
|
||||||
|
|
||||||
|
/** Add a handle that must be cleared during stop. */
|
||||||
|
public void add(RegistrationHandle handle) {
|
||||||
|
handles.add(handle);
|
||||||
|
}
|
||||||
|
|
||||||
/** Add a single listener. */
|
/** Add a single listener. */
|
||||||
public void add(final LifecycleListener listener) {
|
public void add(LifecycleListener listener) {
|
||||||
listeners.put(listener, true);
|
listeners.add(Providers.of(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a single listener. */
|
||||||
|
public void add(Provider<LifecycleListener> listener) {
|
||||||
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add all {@link LifecycleListener}s registered in the Injector. */
|
/** Add all {@link LifecycleListener}s registered in the Injector. */
|
||||||
public void add(final Injector injector) {
|
public void add(Injector injector) {
|
||||||
if (started) {
|
Preconditions.checkState(startedIndex < 0, "Already started");
|
||||||
throw new IllegalStateException("Already started");
|
for (Binding<LifecycleListener> binding : get(injector)) {
|
||||||
}
|
add(binding.getProvider());
|
||||||
for (final Binding<LifecycleListener> binding : get(injector)) {
|
|
||||||
add(binding.getProvider().get());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add all {@link LifecycleListener}s registered in the Injectors. */
|
/** Add all {@link LifecycleListener}s registered in the Injectors. */
|
||||||
public void add(final Injector... injectors) {
|
public void add(Injector... injectors) {
|
||||||
for (final Injector i : injectors) {
|
for (Injector i : injectors) {
|
||||||
add(i);
|
add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start all listeners, in the order they were registered. */
|
/** Start all listeners, in the order they were registered. */
|
||||||
public void start() {
|
public void start() {
|
||||||
if (!started) {
|
for (int i = startedIndex + 1; i < listeners.size(); i++) {
|
||||||
started = true;
|
LifecycleListener listener = listeners.get(i).get();
|
||||||
for (LifecycleListener obj : listeners.keySet()) {
|
startedIndex = i;
|
||||||
obj.start();
|
listener.start();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop all listeners, in the reverse order they were registered. */
|
/** Stop all listeners, in the reverse order they were registered. */
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (started) {
|
for (int i = handles.size() - 1; 0 <= i; i--) {
|
||||||
final List<LifecycleListener> t =
|
handles.get(i).remove();
|
||||||
new ArrayList<LifecycleListener>(listeners.keySet());
|
}
|
||||||
|
handles.clear();
|
||||||
|
|
||||||
for (int i = t.size() - 1; 0 <= i; i--) {
|
for (int i = startedIndex; 0 <= i; i--) {
|
||||||
final LifecycleListener obj = t.get(i);
|
LifecycleListener obj = listeners.get(i).get();
|
||||||
try {
|
try {
|
||||||
obj.stop();
|
obj.stop();
|
||||||
} catch (Throwable err) {
|
} catch (Throwable err) {
|
||||||
LoggerFactory.getLogger(obj.getClass()).warn("Failed to stop", err);
|
LoggerFactory.getLogger(obj.getClass()).warn("Failed to stop", err);
|
||||||
}
|
}
|
||||||
}
|
startedIndex = i - 1;
|
||||||
|
|
||||||
started = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Binding<LifecycleListener>> get(Injector i) {
|
private static List<Binding<LifecycleListener>> get(Injector i) {
|
||||||
return i.findBindingsByType(new TypeLiteral<LifecycleListener>() {});
|
return i.findBindingsByType(new TypeLiteral<LifecycleListener>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> newList() {
|
||||||
|
return Lists.newArrayListWithCapacity(4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.annotations.PluginData;
|
|||||||
import com.google.gerrit.extensions.annotations.PluginName;
|
import com.google.gerrit.extensions.annotations.PluginName;
|
||||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
|
import com.google.gerrit.extensions.registration.RegistrationHandle;
|
||||||
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
|
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
|
||||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
|
||||||
import com.google.gerrit.lifecycle.LifecycleManager;
|
import com.google.gerrit.lifecycle.LifecycleManager;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
@@ -233,28 +232,14 @@ public class Plugin {
|
|||||||
return httpInjector;
|
return httpInjector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(final RegistrationHandle handle) {
|
public void add(RegistrationHandle handle) {
|
||||||
if (handle instanceof ReloadableRegistrationHandle) {
|
if (handle instanceof ReloadableRegistrationHandle) {
|
||||||
if (reloadableHandles == null) {
|
if (reloadableHandles == null) {
|
||||||
reloadableHandles = Lists.newArrayList();
|
reloadableHandles = Lists.newArrayList();
|
||||||
}
|
}
|
||||||
reloadableHandles.add((ReloadableRegistrationHandle<?>) handle);
|
reloadableHandles.add((ReloadableRegistrationHandle<?>) handle);
|
||||||
}
|
}
|
||||||
|
manager.add(handle);
|
||||||
add(new LifecycleListener() {
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
handle.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(LifecycleListener listener) {
|
|
||||||
manager.add(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ReloadableRegistrationHandle<?>> getReloadableHandles() {
|
List<ReloadableRegistrationHandle<?>> getReloadableHandles() {
|
||||||
|
|||||||
Reference in New Issue
Block a user