/* * Copyright 2015 Robert von Burg <eitch@eitchnet.ch> * * 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 li.strolch.service.executor; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchComponent; import li.strolch.exception.StrolchException; import li.strolch.privilege.model.Certificate; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.service.api.Service; import li.strolch.service.api.ServiceArgument; import li.strolch.service.api.ServiceHandler; import li.strolch.service.api.ServiceResult; import li.strolch.utils.helper.ExceptionHelper; /** * The {@link ServiceExecutionHandler} is used to perform long running services so that no singletons etc. are required. * * @author Robert von Burg <eitch@eitchnet.ch> */ public class ServiceExecutionHandler extends StrolchComponent { private Map<String, ServiceExecutionStatus> serviceContextMap; private BlockingQueue<ServiceContext<? extends ServiceArgument, ? extends ServiceResult>> queue; private Thread thread; private volatile boolean interrupted; public ServiceExecutionHandler(ComponentContainer container, String componentName) { super(container, componentName); } @Override public void initialize(ComponentConfiguration configuration) throws Exception { this.serviceContextMap = Collections.synchronizedMap(new HashMap<>()); this.queue = new LinkedBlockingQueue<>(); this.thread = new Thread(new Runnable() { @Override public void run() { try { while (!interrupted) { doService(queue.take()); } } catch (InterruptedException ex) { logger.error(ExceptionHelper.formatExceptionMessage(ex)); } } }, "ServiceExecutor"); this.thread.setDaemon(true); super.initialize(configuration); } private <T extends ServiceArgument, U extends ServiceResult> void doService(ServiceContext<T, U> svcCtx) { if (this.interrupted) return; String serviceName = svcCtx.service.getClass().getName(); ServiceExecutionStatus status = this.serviceContextMap.get(serviceName); status.started(); ServiceHandler svcHandler = getContainer().getComponent(ServiceHandler.class); U svcResult = svcHandler.doService(svcCtx.certificate, svcCtx.service, svcCtx.argument); status.setResult(svcResult); } @Override public void start() throws Exception { this.thread.start(); super.start(); } @Override public void stop() throws Exception { if (this.thread != null) { this.thread.interrupt(); try { this.thread.join(2000l); } catch (InterruptedException e) { logger.error(e.getMessage()); } } super.stop(); } @Override public void destroy() throws Exception { this.thread = null; super.destroy(); } public ServiceExecutionStatus getStatus(Class<?> clazz) { ServiceExecutionStatus status = this.serviceContextMap.get(clazz.getName()); if (status == null) return new ServiceExecutionStatus(clazz.getName()); return status; } public <T extends ServiceArgument, U extends ServiceResult> ServiceExecutionStatus doService( Certificate certificate, Service<T, U> service, T argument) { String serviceName = service.getClass().getName(); if (this.serviceContextMap.containsKey(serviceName)) { ServiceExecutionStatus serviceExecutionStatus = this.serviceContextMap.get(serviceName); if (!serviceExecutionStatus.isDone()) { throw new StrolchException("A service with name " + serviceName + " is already running!"); } } ServiceContext<T, U> svcCtx = new ServiceContext<>(certificate, service, argument); try { ServiceExecutionStatus status = new ServiceExecutionStatus(serviceName); this.serviceContextMap.put(serviceName, status); this.queue.put(svcCtx); Thread.sleep(20l); return status; } catch (InterruptedException e) { this.serviceContextMap.remove(serviceName); throw new StrolchException("Failed to register service context: " + e.getMessage(), e); } } public class ServiceContext<T extends ServiceArgument, U extends ServiceResult> { private Certificate certificate; private Service<T, U> service; private T argument; public ServiceContext(Certificate certificate, Service<T, U> service, T argument) { this.certificate = certificate; this.service = service; this.argument = argument; } } }