/******************************************************************************* * Copyright 2013-2014 alladin-IT GmbH * * 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 at.alladin.rmbt.qos.testserver.service; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import at.alladin.rmbt.qos.testserver.ServerPreferences.TestServerServiceEnum; import at.alladin.rmbt.qos.testserver.service.AbstractJob.JobState; import at.alladin.rmbt.qos.testserver.service.EventJob.EventType; import at.alladin.rmbt.qos.testserver.util.TestServerConsole; /** * * @author lb * */ public class ServiceManager { public final static String TAG = ServiceManager.class.getSimpleName() + ": "; /** * * @author lb * */ public static class FutureService { protected final Future<?> future; protected final AbstractJob<?> service; public FutureService(Future<?> future, AbstractJob<?> service) { this.future = future; this.service = service; } public Future<?> getFuture() { return future; } public AbstractJob<?> getService() { return service; } } /** * */ private final ExecutorService executor = Executors.newCachedThreadPool(); /** * */ private final ConcurrentMap<String, FutureService> serviceMap = new ConcurrentHashMap<>(); /** * * @param service */ public void addService(AbstractJob<?> service) { if (service instanceof IntervalJob<?>) { //in case of an error check if the job should be restarted service.setCallback(JobState.ERROR, new JobCallback () { @Override public boolean onEvent(AbstractJob<?> service, JobState state, Object newResult) { if (JobState.ERROR.equals(state) && ((IntervalJob<?>) service).restartOnError()) { synchronized (serviceMap) { log("Forcing service restart after error.", 0, service.getService()); final FutureService fs = serviceMap.get(service.getService()); if (fs != null && fs.getService().getIsRunning()) { log("Service still running. Waiting for termination...", 0, service.getService()); fs.getFuture().cancel(true); } else { final AbstractJob<?> newService = service.getNewInstance(); /* * to prevent infinite loops with this callback involved it is not added to * a service that didn't run at least 1 time before the error occurred */ if (((IntervalJob<?>) service).getExecutionCounter() >= 1) { newService.setCallback(JobState.ERROR, this); } else { service.log("Beware: This service did not complete at least 1 run successfully. Another error won't restart it again. This is the last chance!", 0); } final Future<?> future = executor.submit(newService); serviceMap.replace(newService.getId(), new FutureService(future, newService)); } } return true; } return false; } }); final Future<?> future = executor.submit(service); serviceMap.putIfAbsent(service.getId(), new FutureService(future, service)); } else if (service instanceof EventJob<?>) { serviceMap.putIfAbsent(service.getId(), new FutureService(null, service)); } } /** * attempts to stop all running services and returns all results with their service names */ public Map<String, Object> shutdownAll(boolean mayInterruptIfRunning) { Map<String, Object> resultMap = new HashMap<>(); for (Entry<String, FutureService> e : serviceMap.entrySet()) { try { if (e.getValue().getFuture() != null) { if (!e.getValue().getFuture().isDone()) { e.getValue().getService().stop(); if (mayInterruptIfRunning) { e.getValue().getService().interrupt(); } } } else { e.getValue().getService().stop(); } Object result; result = e.getValue().getService().getResult(); resultMap.put(e.getKey(), result); } catch (Exception ex) { ex.printStackTrace(); } } return resultMap; } /** * */ public void shutdownAllNow() { executor.shutdownNow(); } /** * * @param type */ public void dispatchEvent(EventType type) { for (FutureService serviceEntry : serviceMap.values()) { if (serviceEntry.getFuture() == null && serviceEntry.getService() instanceof EventJob<?>) { if (((EventJob<?>)serviceEntry.getService()).shouldLaunch(type)) { executor.execute(serviceEntry.getService()); } } } } /** * attempts to stop a service and return its result * @param serviceName * @return */ public Object shutdownService(String serviceName) { FutureService service = serviceMap.remove(serviceName); if (service != null) { try { service.getService().stop(); return service.future.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } return null; } /** * * @return */ public ConcurrentMap<String, FutureService> getServiceMap() { return serviceMap; } /** * * @param log * @param verboseLevelNeeded * @param service */ public void log(String log, int verboseLevelNeeded, TestServerServiceEnum service) { TestServerConsole.log(TAG + log, verboseLevelNeeded, service); } }