diff options
Diffstat (limited to 'utils/src/main/java')
-rw-r--r-- | utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java | 190 | ||||
-rw-r--r-- | utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java | 44 |
2 files changed, 234 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; + } +} diff --git a/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java new file mode 100644 index 00000000..3daa441a --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java @@ -0,0 +1,44 @@ +/* + * ============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; + +/** + * Exceptions thrown by the ServiceManager. + */ +public class ServiceManagerException extends Exception { + private static final long serialVersionUID = 1L; + + public ServiceManagerException() { + super(); + } + + public ServiceManagerException(String message) { + super(message); + } + + public ServiceManagerException(Throwable cause) { + super(cause); + } + + public ServiceManagerException(String message, Throwable cause) { + super(message, cause); + } +} |