| /* |
| * Copyright (c) 2017 CEA. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * CEA - initial API and implementation |
| */ |
| package org.eclipse.sensinact.gateway.generic; |
| |
| import org.eclipse.sensinact.gateway.common.bundle.Mediator; |
| import org.eclipse.sensinact.gateway.core.ResourceConfig; |
| import org.eclipse.sensinact.gateway.core.method.AccessMethod; |
| import org.eclipse.sensinact.gateway.util.JSONUtils; |
| import org.eclipse.sensinact.gateway.util.UriUtils; |
| |
| import java.util.Deque; |
| import java.util.LinkedList; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| /** |
| * A generic {@link Task} implementation |
| * |
| * @author <a href="mailto:christophe.munilla@cea.fr">Christophe Munilla</a> |
| */ |
| public abstract class TaskImpl implements Task { |
| protected final Object lock = new Object(); |
| protected final Mediator mediator; |
| |
| protected final ResourceConfig resourceConfig; |
| protected final Object[] parameters; |
| protected final TaskTranslator transmitter; |
| protected final String path; |
| protected final String profileId; |
| protected String taskIdentifier; |
| protected Object result; |
| protected long launched; |
| protected long executed; |
| |
| protected long timestamp; |
| |
| protected final AtomicBoolean available; |
| protected final AtomicLong timeout; |
| |
| protected LifecycleStatus status; |
| private Deque<TaskCallBack> callbacks; |
| protected CommandType command; |
| |
| /** |
| * Constructor |
| * |
| * @param mediator the associated {@link Mediator} |
| * @param transmitter the {@link TaskTranslator} in charge of sending the requests |
| * based on the task to instantiate |
| * @param path String path of the {@link SnaObject} which has created the task |
| * to instantiate |
| * @param resourceConfig the {@link ResourceConfig} mapped to the {@link ExtResourceImpl} |
| * on which the task applies |
| * @param parameters the objects array parameterizing the call |
| */ |
| public TaskImpl(Mediator mediator, CommandType command, TaskTranslator transmitter, String path, String profileId, ResourceConfig resourceConfig, Object[] parameters) { |
| this.mediator = mediator; |
| this.available = new AtomicBoolean(false); |
| this.transmitter = transmitter; |
| this.path = path; |
| this.profileId = profileId; |
| this.resourceConfig = resourceConfig; |
| this.parameters = parameters; |
| this.command = command; |
| |
| String serviceProviderId = null; |
| String service = null; |
| String resource = null; |
| String attribute = null; |
| |
| String[] pathElements = UriUtils.getUriElements(path); |
| switch (pathElements.length) { |
| case 4: |
| attribute = pathElements[3]; |
| case 3: |
| resource = pathElements[2]; |
| case 2: |
| service = pathElements[1]; |
| case 1: |
| serviceProviderId = pathElements[0]; |
| default: |
| break; |
| } |
| this.timeout = new AtomicLong(Task.DEFAULT_TIMEOUT); |
| this.status = LifecycleStatus.INITIALIZED; |
| this.callbacks = new LinkedList<TaskCallBack>(); |
| if (serviceProviderId != null) { |
| StringBuilder buffer = new StringBuilder(); |
| buffer.append(serviceProviderId); |
| buffer.append(TaskManager.IDENTIFIER_SEP_CHAR); |
| buffer.append(this.getCommand().name()); |
| |
| if (service != null) { |
| buffer.append(TaskManager.IDENTIFIER_SEP_CHAR); |
| buffer.append(service); |
| } |
| if (resource != null) { |
| buffer.append(TaskManager.IDENTIFIER_SEP_CHAR); |
| buffer.append(resource); |
| |
| } |
| if (attribute != null) { |
| buffer.append(TaskManager.IDENTIFIER_SEP_CHAR); |
| buffer.append(attribute); |
| } |
| this.setTaskIdentifier(buffer.toString()); |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getCommand() |
| */ |
| @Override |
| public CommandType getCommand() { |
| return this.command; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#isDirect() |
| */ |
| @Override |
| public boolean isDirect() { |
| return false; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getResourceConfig() |
| */ |
| @Override |
| public ResourceConfig getResourceConfig() { |
| return this.resourceConfig; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getParameters() |
| */ |
| @Override |
| public Object[] getParameters() { |
| return this.parameters; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getPath() |
| */ |
| @Override |
| public String getPath() { |
| return this.path; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getProfile() |
| */ |
| @Override |
| public String getProfile() { |
| return this.profileId; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getLifecycleStatus() |
| */ |
| @Override |
| public LifecycleStatus getLifecycleStatus() { |
| LifecycleStatus status = null; |
| synchronized (this.status) { |
| status = this.status; |
| } |
| if (this.mediator.isDebugLoggable()) { |
| this.mediator.debug(new StringBuilder("Task status ").append(status).append("[").append(System.currentTimeMillis()).append("]").toString()); |
| } |
| return status; |
| } |
| |
| /** |
| * Defines the current life cycle status value |
| * |
| * @param status the current {@link LifecycleStatus} to set |
| */ |
| protected void setLifecycleStatus(LifecycleStatus status) { |
| synchronized (this.status) { |
| this.status = status; |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#setTaskIdentifier(java.lang.String) |
| */ |
| public void setTaskIdentifier(String taskIdentifier) { |
| this.taskIdentifier = taskIdentifier; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getTaskIdentifier() |
| */ |
| public String getTaskIdentifier() { |
| return this.taskIdentifier; |
| } |
| |
| /** |
| * @InheriDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#setResult(java.lang.Object) |
| */ |
| @Override |
| public void setResult(Object result) { |
| this.setResult(result, System.currentTimeMillis()); |
| } |
| |
| /** |
| * @InheriDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#setResult(java.lang.Object, long) |
| */ |
| @Override |
| public void setResult(Object result, long timestamp) { |
| if (isResultAvailable()) { |
| if (this.mediator.isWarningLoggable()) { |
| this.mediator.warn(new StringBuilder("result already set [").append(this).append("][current: ").append((this.result == AccessMethod.EMPTY ? "EMPTY" : this.result)).append("][new = ").append((result == AccessMethod.EMPTY ? "EMPTY" : result + "]")).toString()); |
| } |
| return; |
| } |
| this.result = result; |
| this.timestamp = timestamp; |
| this.setLifecycleStatus(LifecycleStatus.EXECUTED); |
| this.executed = System.currentTimeMillis(); |
| |
| synchronized (this.lock) { |
| this.available.set(true); |
| } |
| while (!this.callbacks.isEmpty()) { |
| callbacks.pop().callback(this); |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#abort(java.lang.Object) |
| */ |
| public void abort(Object result) { |
| this.result = result; |
| this.setLifecycleStatus(LifecycleStatus.ABORDED); |
| this.executed = System.currentTimeMillis(); |
| |
| synchronized (this.lock) { |
| this.available.set(true); |
| } |
| while (!this.callbacks.isEmpty()) { |
| callbacks.pop().callback(this); |
| } |
| } |
| |
| /** |
| * @InheriDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getResult() |
| */ |
| @Override |
| public Object getResult() { |
| if (!isResultAvailable()) { |
| if (this.mediator.isDebugLoggable()) { |
| this.mediator.debug("result is not available"); |
| } |
| return null; |
| } |
| return this.result; |
| } |
| |
| /** |
| * @InheriDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getTimestamp() |
| */ |
| @Override |
| public long getTimestamp() { |
| if (!isResultAvailable()) { |
| if (this.mediator.isDebugLoggable()) { |
| this.mediator.debug("result is not available"); |
| } |
| return -1; |
| } |
| return this.timestamp; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#isResultAvailable() |
| */ |
| @Override |
| public boolean isResultAvailable() { |
| boolean available; |
| synchronized (this.lock) { |
| available = this.available.get(); |
| } |
| return available; |
| } |
| |
| /** |
| * Returns the delay of execution of this |
| * task |
| * |
| * @return the delay of execution of this |
| * task |
| */ |
| public long getExecutionDelay() { |
| if (getLifecycleStatus() != LifecycleStatus.EXECUTED) { |
| return -1; |
| } |
| return this.executed - this.launched; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#getTimeout() |
| */ |
| public long getTimeout() { |
| return this.timeout.longValue(); |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#setTimeout(long) |
| */ |
| public void setTimeout(long timeout) { |
| this.timeout.set(timeout); |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#registerCallBack(org.eclipse.sensinact.gateway.generic.TaskCallBack) |
| */ |
| public void registerCallBack(TaskCallBack callback) { |
| if (callback == null) { |
| return; |
| } |
| synchronized (this.lock) { |
| if (this.available.get()) { |
| callback.callback(this); |
| |
| } else { |
| this.callbacks.offer(callback); |
| } |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.generic.Task#execute() |
| */ |
| @Override |
| public void execute() { |
| this.setLifecycleStatus(LifecycleStatus.LAUNCHED); |
| this.launched = System.currentTimeMillis(); |
| this.transmitter.send(this); |
| } |
| |
| /** |
| * @InheritDoc |
| * @see org.eclipse.sensinact.gateway.common.primitive.JSONable#getJSON() |
| */ |
| public String getJSON() { |
| if (!this.isResultAvailable()) { |
| return JSONUtils.EMPTY; |
| } |
| StringBuilder builder = new StringBuilder(); |
| builder.append(JSONUtils.OPEN_BRACE); |
| builder.append(JSONUtils.QUOTE); |
| builder.append("uri"); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COLON); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(this.path); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COMMA); |
| builder.append(JSONUtils.QUOTE); |
| builder.append("task"); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COLON); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(this.getCommand().name()); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COMMA); |
| builder.append(JSONUtils.QUOTE); |
| builder.append("start"); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COLON); |
| builder.append(this.launched); |
| builder.append(JSONUtils.COMMA); |
| builder.append(JSONUtils.QUOTE); |
| builder.append("end"); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COLON); |
| builder.append(this.executed); |
| builder.append(JSONUtils.COMMA); |
| builder.append(JSONUtils.QUOTE); |
| builder.append("status"); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COLON); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(this.status.name()); |
| builder.append(JSONUtils.QUOTE); |
| if (!LifecycleStatus.ABORDED.equals(this.status)) { |
| builder.append(JSONUtils.COMMA); |
| builder.append(JSONUtils.QUOTE); |
| builder.append("result"); |
| builder.append(JSONUtils.QUOTE); |
| builder.append(JSONUtils.COLON); |
| builder.append(JSONUtils.toJSONFormat(this.result)); |
| } |
| builder.append(JSONUtils.CLOSE_BRACE); |
| return builder.toString(); |
| } |
| } |