Permalink
Browse files

Add StabilityMonitor mechanism, with tests

  • Loading branch information...
1 parent d84e8d1 commit b8b8f47661f8e216facdc79779b5b02ca948ff25 @dmlloyd dmlloyd committed Nov 21, 2012
@@ -73,6 +73,10 @@
*/
private final IdentityHashMap<ServiceListener<? super S>, ServiceListener.Inheritance> listeners;
/**
+ * The set of registered stability monitors.
+ */
+ private final IdentityHashSet<StabilityMonitor> monitors;
+ /**
* The primary registration of this service.
*/
private final ServiceRegistrationImpl primaryRegistration;
@@ -187,6 +191,7 @@
this.primaryRegistration = primaryRegistration;
this.aliasRegistrations = aliasRegistrations;
this.listeners = new IdentityHashMap<ServiceListener<? super S>, ServiceListener.Inheritance>(listeners);
+ this.monitors = new IdentityHashSet<StabilityMonitor>();
this.parent = parent;
int depCount = dependencies.length;
unstartedDependencies = 0;
@@ -340,10 +345,16 @@ void updateStabilityState(int oldVal) {
if (oldVal == 0) {
if (asyncTasks > 0 || ! state.isRestState()) {
primaryRegistration.getContainer().incrementUnstableServices();
+ for (StabilityMonitor monitor : monitors) {
+ monitor.incrementUnstableServices();
+ }
}
} else {
if (asyncTasks == 0 && state.isRestState()) {
primaryRegistration.getContainer().decrementUnstableServices();
+ for (StabilityMonitor monitor : monitors) {
+ monitor.decrementUnstableServices();
+ }
}
}
}
@@ -553,6 +564,9 @@ void transition(final ArrayList<Runnable> tasks) {
}
case START_REQUESTED_to_PROBLEM: {
getPrimaryRegistration().getContainer().addProblem(this);
+ for (StabilityMonitor monitor : monitors) {
+ monitor.addProblem(this);
+ }
if (!immediateUnavailableDependencies.isEmpty()) {
getListenerTasks(ListenerNotification.IMMEDIATE_DEPENDENCY_UNAVAILABLE, tasks);
}
@@ -583,6 +597,9 @@ void transition(final ArrayList<Runnable> tasks) {
}
case STARTING_to_START_FAILED: {
getPrimaryRegistration().getContainer().addFailed(this);
+ for (StabilityMonitor monitor : monitors) {
+ monitor.addFailed(this);
+ }
ChildServiceTarget childTarget = this.childTarget;
if (childTarget != null) {
childTarget.valid = false;
@@ -594,6 +611,9 @@ void transition(final ArrayList<Runnable> tasks) {
}
case START_FAILED_to_STARTING: {
getPrimaryRegistration().getContainer().removeFailed(this);
+ for (StabilityMonitor monitor : monitors) {
+ monitor.removeFailed(this);
+ }
getListenerTasks(transition, tasks);
tasks.add(new DependencyRetryingTask(getDependents()));
tasks.add(new DependentStartedTask());
@@ -606,6 +626,9 @@ void transition(final ArrayList<Runnable> tasks) {
}
case START_FAILED_to_DOWN: {
getPrimaryRegistration().getContainer().removeFailed(this);
+ for (StabilityMonitor monitor : monitors) {
+ monitor.removeFailed(this);
+ }
startException = null;
failCount--;
getListenerTasks(transition, tasks);
@@ -660,6 +683,9 @@ void transition(final ArrayList<Runnable> tasks) {
}
case PROBLEM_to_START_REQUESTED: {
getPrimaryRegistration().getContainer().removeProblem(this);
+ for (StabilityMonitor monitor : monitors) {
+ monitor.removeProblem(this);
+ }
if (!immediateUnavailableDependencies.isEmpty()) {
getListenerTasks(ListenerNotification.IMMEDIATE_DEPENDENCY_AVAILABLE, tasks);
}
@@ -1445,6 +1471,29 @@ String dumpServiceDetails() {
return b.toString();
}
+ void addMonitor(final StabilityMonitor stabilityMonitor) {
+ assert !holdsLock(this);
+ synchronized (this) {
+ final Substate state = this.state;
+ monitors.add(stabilityMonitor);
+ if (getStabilityState() != 0) {
+ stabilityMonitor.incrementUnstableServices();
+ if (state == Substate.START_FAILED) {
+ stabilityMonitor.addFailed(this);
+ } else if (state == Substate.PROBLEM) {
+ stabilityMonitor.addProblem(this);
+ }
+ }
+ }
+ }
+
+ void removeMonitor(final StabilityMonitor stabilityMonitor) {
+ assert !holdsLock(this);
+ synchronized (this) {
+ monitors.remove(stabilityMonitor);
+ }
+ }
+
private enum ListenerNotification {
/** Notify the listener that is has been added. */
LISTENER_ADDED,
@@ -0,0 +1,138 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.msc.service;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.Thread.holdsLock;
+
+/**
+ * A stability monitor for satisfying certain AS use cases.
+ *
+ * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
+ */
+public final class StabilityMonitor {
+
+ private final Object lock = new Object();
+ private int unstableServices;
+ private final Set<ServiceController<?>> problems = new IdentityHashSet<ServiceController<?>>();
+ private final Set<ServiceController<?>> failed = new IdentityHashSet<ServiceController<?>>();
+ private final ArrayList<ServiceController<?>> controllers = new ArrayList<ServiceController<?>>();
+
+ public void addController(ServiceController<?> controller) {
+ final ServiceControllerImpl<?> serviceController = (ServiceControllerImpl<?>) controller;
+ serviceController.addMonitor(this);
+ }
+
+ public void remove() {
+ for (ServiceController<?> controller : controllers) {
+ final ServiceControllerImpl<?> serviceController = (ServiceControllerImpl<?>) controller;
+ serviceController.removeMonitor(this);
+ }
+ }
+
+ public void awaitStability(Set<? super ServiceController<?>> failed, Set<? super ServiceController<?>> problem) throws InterruptedException {
+ synchronized (lock) {
+ while (unstableServices != 0) {
+ lock.wait();
+ }
+ if (failed != null) {
+ failed.addAll(this.failed);
+ }
+ if (problem != null) {
+ problem.addAll(this.problems);
+ }
+ }
+ }
+
+ public boolean awaitStability(final long timeout, final TimeUnit unit, Set<? super ServiceController<?>> failed, Set<? super ServiceController<?>> problem) throws InterruptedException {
+ long now = System.nanoTime();
+ long remaining = unit.toNanos(timeout);
+ synchronized (lock) {
+ while (unstableServices != 0) {
+ if (remaining <= 0L) {
+ return false;
+ }
+ lock.wait(remaining / 1000000L, (int) (remaining % 1000000L));
+ remaining -= (-now + (now = System.nanoTime()));
+ }
+ if (failed != null) {
+ failed.addAll(this.failed);
+ }
+ if (problem != null) {
+ problem.addAll(this.problems);
+ }
+ return true;
+ }
+ }
+
+ void removeProblem(ServiceController<?> controller) {
+ assert holdsLock(controller);
+ synchronized (lock) {
+ problems.remove(controller);
+ }
+ }
+
+ void removeFailed(ServiceController<?> controller) {
+ assert holdsLock(controller);
+ synchronized (lock) {
+ failed.remove(controller);
+ }
+ }
+
+ void incrementUnstableServices() {
+ synchronized (lock) {
+ unstableServices++;
+ }
+ }
+
+ void addProblem(ServiceController<?> controller) {
+ assert holdsLock(controller);
+ synchronized (lock) {
+ problems.add(controller);
+ if (--unstableServices == 0) {
+ lock.notifyAll();
+ }
+ }
+ }
+
+ void addFailed(ServiceController<?> controller) {
+ assert holdsLock(controller);
+ synchronized (lock) {
+ failed.add(controller);
+ if (--unstableServices == 0) {
+ lock.notifyAll();
+ }
+ }
+ }
+
+ void decrementUnstableServices() {
+ synchronized (lock) {
+ if (--unstableServices == 0) {
+ lock.notifyAll();
+ }
+ }
+ }
+}
@@ -24,7 +24,6 @@
import java.beans.ConstructorProperties;
import java.io.Serializable;
-import org.jboss.msc.service.StartException;
/**
* A representation of the current status of some service.
@@ -85,4 +85,63 @@ public Object getValue() throws IllegalStateException, IllegalArgumentException
assertTrue(problem.isEmpty());
assertTrue(failed.isEmpty());
}
+
+ @Test
+ public void testSimpleInstallation3() {
+ final ServiceBuilder<Void> builder = serviceContainer.addService(ServiceName.of("Test1"), Service.NULL);
+ final ServiceController<Void> controller = builder.install();
+ final StabilityMonitor stabilityMonitor = new StabilityMonitor();
+ stabilityMonitor.addController(controller);
+ final Set<Object> problem = new IdentityHashSet<Object>();
+ final Set<Object> failed = new IdentityHashSet<Object>();
+ try {
+ stabilityMonitor.awaitStability(failed, problem);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ assertController(controller.getName(), controller);
+ assertTrue(problem.isEmpty());
+ assertTrue(failed.isEmpty());
+ }
+
+ @Test
+ public void testSimpleInstallation4() {
+ final StabilityMonitor stabilityMonitor = new StabilityMonitor();
+ ServiceBuilder<?> builder = serviceContainer.addService(ServiceName.of("Test1"), new Service<Object>() {
+ public void start(final StartContext context) throws StartException {
+ final ServiceBuilder<Void> builder = context.getChildTarget().addService(ServiceName.of("Test1.child"), NULL);
+ builder.addListener(new AbstractServiceListener<Void>() {
+ public void transition(final ServiceController<? extends Void> controller, final ServiceController.Transition transition) {
+ // blah
+ }
+ });
+ builder.install();
+ }
+
+ public void stop(final StopContext context) {
+ }
+
+ public Object getValue() throws IllegalStateException, IllegalArgumentException {
+ return null;
+ }
+ });
+ builder.addDependencies(ServiceName.of("Test2"));
+ final ServiceController<?> controller1 = builder.install();
+ stabilityMonitor.addController(controller1);
+ builder = serviceContainer.addService(ServiceName.of("Test2"), Service.NULL);
+ builder.setInitialMode(ServiceController.Mode.ON_DEMAND);
+ final ServiceController<?> controller2 = builder.install();
+ stabilityMonitor.addController(controller2);
+ final Set<Object> problem = new IdentityHashSet<Object>();
+ final Set<Object> failed = new IdentityHashSet<Object>();
+ try {
+ stabilityMonitor.awaitStability(failed, problem);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ assertController(controller1.getName(), controller1);
+ assertController(controller2.getName(), controller2);
+ assertTrue(problem.isEmpty());
+ assertTrue(failed.isEmpty());
+ }
}

0 comments on commit b8b8f47

Please sign in to comment.