[New] Added new ServiceExecutionHandler
The ServiceExecutionHandler is used to execute long running services without needing singletons
This commit is contained in:
parent
f46e72bbdb
commit
a7de76933b
|
@ -0,0 +1,142 @@
|
||||||
|
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.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 ch.eitchnet.privilege.model.Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
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(ex.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "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() {
|
||||||
|
this.thread.start();
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
|
||||||
|
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() {
|
||||||
|
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<T, U>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package li.strolch.service.executor;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
import li.strolch.service.api.ServiceResult;
|
||||||
|
import ch.eitchnet.utils.helper.StringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
|
*/
|
||||||
|
@XmlRootElement(name = "ServiceExecutionStatus")
|
||||||
|
@XmlAccessorType(XmlAccessType.NONE)
|
||||||
|
public class ServiceExecutionStatus {
|
||||||
|
|
||||||
|
private String serviceName;
|
||||||
|
private volatile boolean started;
|
||||||
|
private volatile ServiceResult result;
|
||||||
|
|
||||||
|
public ServiceExecutionStatus() {
|
||||||
|
// no arg constructor for JAXB
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceExecutionStatus(String serviceName) {
|
||||||
|
this.serviceName = serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAttribute(name = "serviceName")
|
||||||
|
public String getServiceName() {
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized ServiceResult getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setResult(ServiceResult svcResult) {
|
||||||
|
this.result = svcResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAttribute(name = "resultMessage")
|
||||||
|
public String getResultMessage() {
|
||||||
|
if (this.result == null)
|
||||||
|
return StringHelper.DASH;
|
||||||
|
if (this.result.getMessage() == null)
|
||||||
|
return StringHelper.DASH;
|
||||||
|
return this.result.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAttribute(name = "resultState")
|
||||||
|
public String getResultState() {
|
||||||
|
if (this.result == null)
|
||||||
|
return StringHelper.DASH;
|
||||||
|
return this.result.getState().name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAttribute(name = "done")
|
||||||
|
public boolean isDone() {
|
||||||
|
return this.result != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAttribute(name = "started")
|
||||||
|
public synchronized boolean isStarted() {
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void started() {
|
||||||
|
this.started = true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue