aboutsummaryrefslogtreecommitdiffstats
path: root/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java')
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java190
1 files changed, 190 insertions, 0 deletions
diff --git a/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java
new file mode 100644
index 00000000..8bf89d56
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java
@@ -0,0 +1,190 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP PAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.services;
+
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import org.onap.policy.common.capabilities.Startable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages a series of services. The services are started in order, and stopped in reverse
+ * order.
+ */
+public class ServiceManager {
+ private static final Logger logger = LoggerFactory.getLogger(ServiceManager.class);
+
+ /**
+ * Services to be started/stopped.
+ */
+ private final Deque<Service> items = new LinkedList<>();
+
+ /**
+ * {@code True} if the services are currently running, {@code false} otherwise.
+ */
+ private boolean running;
+
+ /**
+ * Adds a pair of service actions to the manager.
+ *
+ * @param stepName name to be logged when the service is started/stopped
+ * @param starter function to start the service
+ * @param stopper function to stop the service
+ * @return this manager
+ */
+ public synchronized ServiceManager addAction(String stepName, RunnableWithEx starter, RunnableWithEx stopper) {
+ if (running) {
+ throw new IllegalStateException("services are already running; cannot add " + stepName);
+ }
+
+ items.add(new Service(stepName, starter, stopper));
+ return this;
+ }
+
+ /**
+ * Adds a service to the manager. The manager will invoke the service's
+ * {@link Startable#start()} and {@link Startable#stop()} methods.
+ *
+ * @param stepName name to be logged when the service is started/stopped
+ * @param service object to be started/stopped
+ * @return this manager
+ */
+ public synchronized ServiceManager addService(String stepName, Startable service) {
+ if (running) {
+ throw new IllegalStateException("services are already running; cannot add " + stepName);
+ }
+
+ items.add(new Service(stepName, () -> service.start(), () -> service.stop()));
+ return this;
+ }
+
+ /**
+ * Starts each service, in order. If a service throws an exception, then the
+ * previously started services are stopped, in reverse order.
+ *
+ * @throws ServiceManagerException if a service fails to start
+ */
+ public synchronized void start() throws ServiceManagerException {
+ if (running) {
+ throw new IllegalStateException("services are already running");
+ }
+
+ // tracks the services that have been started so far
+ Deque<Service> started = new LinkedList<>();
+ Exception ex = null;
+
+ for (Service item : items) {
+ try {
+ logger.info("starting {}", item.stepName);
+ item.starter.run();
+ started.add(item);
+
+ } catch (Exception e) {
+ logger.error("failed to start {}; rewinding steps", item.stepName);
+ ex = e;
+ break;
+ }
+ }
+
+ if (ex == null) {
+ running = true;
+ return;
+ }
+
+ // one of the services failed to start - rewind those we've previously started
+ try {
+ rewind(started);
+
+ } catch (ServiceManagerException e) {
+ logger.error("rewind failed", e);
+ }
+
+ throw new ServiceManagerException(ex);
+ }
+
+ /**
+ * Stops the services, in reverse order from which they were started. Stops all of the
+ * services, even if one of the "stop" functions throws an exception. Assumes that
+ * {@link #start()} has completed successfully.
+ *
+ * @throws ServiceManagerException if a service fails to stop
+ */
+ public synchronized void stop() throws ServiceManagerException {
+ if (!running) {
+ throw new IllegalStateException("services are not running");
+ }
+
+ running = false;
+ rewind(items);
+ }
+
+ /**
+ * Rewinds a list of services, stopping them in reverse order. Stops all of the
+ * services, even if one of the "stop" functions throws an exception.
+ *
+ * @param running services that are running, in the order they were started
+ * @throws ServiceManagerException if a service fails to stop
+ */
+ private void rewind(Deque<Service> running) throws ServiceManagerException {
+ Exception ex = null;
+
+ // stop everything, in reverse order
+ Iterator<Service> it = running.descendingIterator();
+ while (it.hasNext()) {
+ Service item = it.next();
+ try {
+ logger.info("stopping {}", item.stepName);
+ item.stopper.run();
+ } catch (Exception e) {
+ logger.error("failed to stop {}", item.stepName);
+ ex = e;
+
+ // do NOT break or re-throw, as we must stop ALL remaining items
+ }
+ }
+
+ if (ex != null) {
+ throw new ServiceManagerException(ex);
+ }
+ }
+
+ /**
+ * Service information.
+ */
+ private static class Service {
+ private String stepName;
+ private RunnableWithEx starter;
+ private RunnableWithEx stopper;
+
+ public Service(String stepName, RunnableWithEx starter, RunnableWithEx stopper) {
+ this.stepName = stepName;
+ this.starter = starter;
+ this.stopper = stopper;
+ }
+ }
+
+ @FunctionalInterface
+ public static interface RunnableWithEx {
+ public void run() throws Exception;
+ }
+}