package cz.cuni.mff.d3s.been.api; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hazelcast.core.EntryListener; import com.hazelcast.core.IMap; import com.hazelcast.core.Member; import com.hazelcast.query.Predicate; import com.hazelcast.query.SqlPredicate; import cz.cuni.mff.d3s.been.bpk.BpkIdentifier; import cz.cuni.mff.d3s.been.cluster.Instance; import cz.cuni.mff.d3s.been.cluster.Names; import cz.cuni.mff.d3s.been.cluster.context.ClusterContext; import cz.cuni.mff.d3s.been.cluster.query.RuntimeInfoPredicate; import cz.cuni.mff.d3s.been.core.benchmark.BenchmarkEntry; import cz.cuni.mff.d3s.been.core.persistence.Entities; import cz.cuni.mff.d3s.been.core.persistence.Entity; import cz.cuni.mff.d3s.been.core.protocol.command.CommandEntry; import cz.cuni.mff.d3s.been.core.protocol.command.CommandEntryState; import cz.cuni.mff.d3s.been.core.protocol.messages.DeleteTaskWrkDirMessage; import cz.cuni.mff.d3s.been.core.ri.RuntimeInfo; import cz.cuni.mff.d3s.been.core.service.ServiceInfo; import cz.cuni.mff.d3s.been.core.task.*; import cz.cuni.mff.d3s.been.datastore.SoftwareStore; import cz.cuni.mff.d3s.been.datastore.SoftwareStoreBuilderFactory; import cz.cuni.mff.d3s.been.debugassistant.DebugAssistant; import cz.cuni.mff.d3s.been.debugassistant.DebugListItem; import cz.cuni.mff.d3s.been.evaluators.EvaluatorResult; import cz.cuni.mff.d3s.been.logging.ServiceLogMessage; import cz.cuni.mff.d3s.been.logging.TaskLogMessage; import cz.cuni.mff.d3s.been.persistence.*; import cz.cuni.mff.d3s.been.persistence.task.PersistentTaskState; import cz.cuni.mff.d3s.been.swrepoclient.SwRepoClient; import cz.cuni.mff.d3s.been.swrepoclient.SwRepoClientFactory; import cz.cuni.mff.d3s.been.swrepository.SWRepositoryServiceInfoConstants; import cz.cuni.mff.d3s.been.swrepository.Versions; import cz.cuni.mff.d3s.been.util.JSONUtils; import cz.cuni.mff.d3s.been.util.JsonException; /** * The implementation of the {@link BeenApi} interface. To instantiate this * class, use {@link BeenApiFactory}. * * @author donarus */ final class BeenApiImpl implements BeenApi { /** class slf4j logger */ private static final Logger log = LoggerFactory.getLogger(BeenApiImpl.class); /** BEEN cluster context instance */ private final ClusterContext clusterContext; /** JSON utility instance for serialization/deserialization */ private final JSONUtils jsonUtils = JSONUtils.newInstance(); /** * Default constructor, which connects to the BEEN cluster as a native * Hazelcast client using the specified connection credentials. There must * already be some running BEEN node in full mode. * * @param host * IP address or hostname of a running node * @param port * port of a running Hazelcast instance of the node * @param groupName * Hazelcast group name * @param groupPassword * Hazelcast group password */ public BeenApiImpl(final String host, final int port, final String groupName, final String groupPassword) { Instance.newNativeInstance(host, port, groupName, groupPassword); clusterContext = Instance.createContext(); } /** * {@link BeenApiImpl} constructor that reuses an already existing * {@link ClusterContext} instance. The passed cluster context must be already * connected to the BEEN cluster. * * @param clusterContext * the cluster context to reuse */ public BeenApiImpl(final ClusterContext clusterContext) { this.clusterContext = clusterContext; } @Override public void shutdown() { clusterContext.stop(); Instance.shutdown(); } @Override public Collection<Member> getClusterMembers() throws BeenApiException { final String errorMsg = "Failed to list connected members"; checkIsActive(errorMsg); try { return clusterContext.getMembers(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public boolean isSwRepositoryOnline() throws BeenApiException { final String errorMsg = "Failed to check if software repository is running"; checkIsActive(errorMsg); try { return clusterContext.getServices().getSWRepositoryInfo() != null; } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public List<ServiceInfo> getClusterServices() throws BeenApiException { final String errorMsg = "Failed to list available cluster services"; checkIsActive(errorMsg); try { return new ArrayList<>(clusterContext.getServices().getServicesMap().values()); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskEntry> getTasks() throws BeenApiException { final String errorMsg = "Failed to list task entries"; checkIsActive(errorMsg); try { return clusterContext.getTasks().getTasks(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskEntry> getTasksOnRuntime(String runtimeId) throws BeenApiException { final String errorMsg = "Failed to list task entries on a Host Runtime " + runtimeId; checkIsActive(errorMsg); try { return clusterContext.getTasks().getTasksOnRuntime(runtimeId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public TaskEntry getTask(final String taskId) throws BeenApiException { final String errorMsg = String.format("Failed to get task with id '%s'", taskId); checkIsActive(errorMsg); try { return clusterContext.getTasks().getTask(taskId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskContextEntry> getTaskContexts() throws BeenApiException { final String errorMsg = "Failed to list task contexts"; checkIsActive(errorMsg); try { return clusterContext.getTaskContexts().getTaskContexts(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public TaskContextEntry getTaskContext(final String taskContextId) throws BeenApiException { final String errorMsg = String.format("Failed to get task context with id '%s'", taskContextId); checkIsActive(errorMsg); try { return clusterContext.getTaskContexts().getTaskContext(taskContextId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<BenchmarkEntry> getBenchmarks() throws BeenApiException { final String errorMsg = "Failed to list benchmarks"; checkIsActive(errorMsg); try { return clusterContext.getBenchmarks().getBenchmarksMap().values(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public BenchmarkEntry getBenchmark(final String benchmarkId) throws BeenApiException { final String errorMsg = String.format("Failed to get benchmark with id '%s'", benchmarkId); checkIsActive(errorMsg); try { return clusterContext.getBenchmarks().get(benchmarkId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskContextEntry> getTaskContextsInBenchmark(final String benchmarkId) throws BeenApiException { final String errorMsg = String.format("Failed to list task contexts for benchmark with id '%s'", benchmarkId); checkIsActive(errorMsg); try { return clusterContext.getBenchmarks().getTaskContextsInBenchmark(benchmarkId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskEntry> getTasksInTaskContext(final String taskContextId) throws BeenApiException { final String errorMsg = String.format("Failed to list tasks for task context with id '%s'", taskContextId); checkIsActive(errorMsg); try { return clusterContext.getTaskContexts().getTasksInTaskContext(taskContextId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<RuntimeInfo> getRuntimes() throws BeenApiException { final String errorMsg = "Failed to list host runtimes"; checkIsActive(errorMsg); try { return clusterContext.getRuntimes().getRuntimes(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<RuntimeInfo> getRuntimes(final String xpath) throws BeenApiException { final String errorMsg = String.format("Failed to get list of runtimes matching '%s'", xpath); checkIsActive(errorMsg); Predicate<?, ?> predicate = new RuntimeInfoPredicate(xpath); try { return clusterContext.getRuntimes().getRuntimeMap().values(predicate); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public RuntimeInfo getRuntime(final String runtimeId) throws BeenApiException { final String errorMsg = String.format("Failed to get host runtime with id '%s'", runtimeId); checkIsActive(errorMsg); try { return clusterContext.getRuntimes().getRuntimeInfo(runtimeId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } // --------------------------------------------------- // TASK/TASK CONTEXT/BENCHMARK DESCRIPTORS PERSISTENCE // --------------------------------------------------- @Override public void saveNamedTaskDescriptor(final TaskDescriptor descriptor, final String name, final BpkIdentifier bpkId) throws BeenApiException { final String errorMsg = String.format( "Failed to save named task descriptors with name '%s' for bpk '%s:%s:%s'", name, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); checkIsActive(errorMsg); final String key = createNamedDescriptorKey(bpkId, name); final NamedTaskDescriptor namedDescriptor = new NamedTaskDescriptor(name, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion(), descriptor); try { clusterContext.getMap(Names.NAMED_TASK_DESCRIPTORS_MAP_NAME).put(key, namedDescriptor); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void deleteNamedTaskDescriptor(final BpkIdentifier bpkId, final String name) throws BeenApiException { final String errorMsg = String.format( "Failed to delete named task descriptors with name '%s' for bpk '%s:%s:%s'", name, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); checkIsActive(errorMsg); final String key = createNamedDescriptorKey(bpkId, name); try { clusterContext.getMap(Names.NAMED_TASK_DESCRIPTORS_MAP_NAME).remove(key); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void saveNamedContextDescriptor(final TaskContextDescriptor descriptor, final String name, final BpkIdentifier bpkId) throws BeenApiException { final String errorMsg = String.format( "Failed to save named task context descriptors with name '%s' for bpk '%s:%s:%s'", name, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); checkIsActive(errorMsg); final String key = createNamedDescriptorKey(bpkId, name); final NamedTaskContextDescriptor namedDescriptor = new NamedTaskContextDescriptor(name, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion(), descriptor); try { clusterContext.getMap(Names.NAMED_TASK_CONTEXT_DESCRIPTORS_MAP_NAME).put(key, namedDescriptor); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void deleteNamedTaskContextDescriptor(final BpkIdentifier bpkId, final String name) throws BeenApiException { final String errorMsg = String.format( "Failed to delete named task context descriptors with name '%s' for bpk '%s:%s:%s'", name, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); checkIsActive(errorMsg); final String key = createNamedDescriptorKey(bpkId, name); try { clusterContext.getMap(Names.NAMED_TASK_CONTEXT_DESCRIPTORS_MAP_NAME).remove(key); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Map<String, TaskDescriptor> getNamedTaskDescriptorsForBpk(final BpkIdentifier bpkIdentifier) throws BeenApiException { final String errorMsg = String.format( "Failed to list named task descriptors for bpk '%s:%s:%s'", bpkIdentifier.getGroupId(), bpkIdentifier.getBpkId(), bpkIdentifier.getVersion()); checkIsActive(errorMsg); final SqlPredicate predicate = new SqlPredicate(String.format( "groupId = '%s' AND bpkId = '%s' and bpkVersion = '%s'", bpkIdentifier.getGroupId(), bpkIdentifier.getBpkId(), bpkIdentifier.getVersion())); Collection<NamedTaskDescriptor> foundDescriptors; try { foundDescriptors = queryHazelcastMap(Names.NAMED_TASK_DESCRIPTORS_MAP_NAME, predicate); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } final Map<String, TaskDescriptor> namedDescriptors = new HashMap<>(); for (NamedTaskDescriptor foundDescriptor : foundDescriptors) { namedDescriptors.put(foundDescriptor.getName(), foundDescriptor.getDescriptor()); } return namedDescriptors; } @Override public Map<String, TaskContextDescriptor> getNamedContextDescriptorsForBpk(final BpkIdentifier bpkIdentifier) throws BeenApiException { final String errorMsg = String.format( "Failed to list named task context descriptors for bpk '%s:%s:%s'", bpkIdentifier.getGroupId(), bpkIdentifier.getBpkId(), bpkIdentifier.getVersion()); checkIsActive(errorMsg); final SqlPredicate predicate = new SqlPredicate(String.format( "groupId = '%s' AND bpkId = '%s' and bpkVersion = '%s'", bpkIdentifier.getGroupId(), bpkIdentifier.getBpkId(), bpkIdentifier.getVersion())); Collection<NamedTaskContextDescriptor> foundDescriptors; try { foundDescriptors = queryHazelcastMap(Names.NAMED_TASK_CONTEXT_DESCRIPTORS_MAP_NAME, predicate); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } final Map<String, TaskContextDescriptor> namedDescriptors = new HashMap<>(); for (NamedTaskContextDescriptor foundDescriptor : foundDescriptors) { namedDescriptors.put(foundDescriptor.getName(), foundDescriptor.getDescriptor()); } return namedDescriptors; } // FIXME - EntryListener is haazelcast dependency !!! @Override public void addLogListener(final EntryListener<String, String> listener) throws BeenApiException { final String errorMsg = "Failed to add task log listener"; checkIsActive(errorMsg); try { clusterContext.<String, String> getMap(Names.LOGS_TASK_MAP_NAME).addEntryListener(listener, true); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void removeLogListener(final EntryListener<String, String> listener) throws BeenApiException { final String errorMsg = "Failed to remove task log listener"; checkIsActive(errorMsg); try { clusterContext.<String, String> getMap(Names.LOGS_TASK_MAP_NAME).removeEntryListener(listener); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskLogMessage> getLogsForTask(final String taskId) throws BeenApiException { final String errorMsg = String.format("Failed to list logs for task with id '%s'", taskId); final Query query = new QueryBuilder().on(Entities.LOG_TASK.getId()).with("taskId", taskId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { List<TaskLogMessage> logs = new ArrayList<>(unpackDataAnswer(query, answer, TaskLogMessage.class)); Comparator<TaskLogMessage> comparator = new Comparator<TaskLogMessage>() { @Override public int compare(TaskLogMessage o1, TaskLogMessage o2) { return new Long(o1.getCreated()).compareTo(o2.getCreated()); } }; Collections.sort(logs, comparator); return logs; } catch (DAOException e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<EvaluatorResult> getEvaluatorResults() throws BeenApiException { final String errorMsg = "Failed to list logs for task"; final Query query = new QueryBuilder().on(Entities.RESULT_EVALUATOR.getId()).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { return unpackDataAnswer(query, answer, EvaluatorResult.class); } catch (DAOException e) { throw createBeenApiException(errorMsg, e); } } @Override public void deleteResult(final String resultId) throws BeenApiException { final String errorMsg = String.format("Failed to delete result with id '%s'", resultId); final Query query = new QueryBuilder().on(Entities.RESULT_EVALUATOR.getId()).with("id", resultId).delete(); final QueryAnswer answer = performQuery(query, errorMsg); if (answer.getStatus() != QueryStatus.OK) { throw createBeenApiException(errorMsg, answer.getStatus().getDescription()); } } @Override public EvaluatorResult getEvaluatorResult(final String resultId) throws BeenApiException { final String errorMsg = String.format("Failed to get evaluator results with id '%s'", resultId); final Query query = new QueryBuilder().on(Entities.RESULT_EVALUATOR.getId()).with("id", resultId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { final Collection<EvaluatorResult> evaluatorResults = unpackDataAnswer(query, answer, EvaluatorResult.class); if (evaluatorResults.size() != 1) { throw createBeenApiException( errorMsg, String.format("Found '%d' results but expected exactly 1 result", evaluatorResults.size())); } return evaluatorResults.iterator().next(); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Collection<BpkIdentifier> getBpks() throws BeenApiException { final String errorMsg = "Failed to list bpks"; checkIsActive(errorMsg); final SwRepoClient client = getSwRepoClient(errorMsg); try { return client.listBpks(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void uploadBpk(final BpkHolder bpkFileHolder) throws BeenApiException { final BpkIdentifier bpkIdentifier; try { bpkIdentifier = bpkFileHolder.getBpkIdentifier(); } catch (Exception e) { throw createBeenApiException("Failed to read bpk info from bpk stream", e); } final String groupId = bpkIdentifier.getGroupId(); final String bpkId = bpkIdentifier.getBpkId(); final String version = bpkIdentifier.getVersion(); final String errorMsg = String.format("Failed to upload bpk '%s:%s:%s'", groupId, bpkId, version); checkIsActive(errorMsg); if (!version.endsWith(Versions.SNAPSHOT_SUFFIX)) { for (BpkIdentifier existingBpk : getBpks()) { if (existingBpk.getBpkId().equals(bpkId) && existingBpk.getGroupId().equals(groupId) && existingBpk.getVersion().equals( version)) { throw createBeenApiException( errorMsg, "Bpk with same identifiers already exists in repository and its version is not '*-SNAPSHOT'. Reupload is not supported."); } } } final SwRepoClient client = getSwRepoClient(errorMsg); try { client.putBpk(bpkIdentifier, bpkFileHolder.getInputStream()); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public InputStream downloadBpk(final BpkIdentifier bpkIdentifier) throws BeenApiException { final String groupId = bpkIdentifier.getGroupId(); final String bpkId = bpkIdentifier.getBpkId(); final String version = bpkIdentifier.getVersion(); final String errorMsg = String.format("Failed to download bpk '%s:%s:%s'", groupId, bpkId, version); checkIsActive(errorMsg); final SwRepoClient client = getSwRepoClient(errorMsg); try { return client.getBpkNoCache(bpkIdentifier).getInputStream(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public final String submitTask(final TaskDescriptor descriptor) throws BeenApiException { final String groupId = descriptor.getGroupId(); final String bpkId = descriptor.getBpkId(); final String version = descriptor.getVersion(); final String errorMsg = String.format("Failed to submit task descriptor '%s:%s:%s'", groupId, bpkId, version); checkIsActive(errorMsg); try { return clusterContext.getTaskContexts().submitTaskInNewContext(descriptor); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public final String submitTaskContext(final TaskContextDescriptor descriptor) throws BeenApiException { final String errorMsg = String.format("Failed to submit task context descriptor '%s'", descriptor.getName()); checkIsActive(errorMsg); try { return clusterContext.getTaskContexts().submit(descriptor, null); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public final String submitTaskContext(final TaskContextDescriptor descriptor, final String benchmarkId) throws BeenApiException { final String errorMsg = String.format( "Failed to submit task context descriptor '%s' for benchmark '%s'", descriptor.getName(), benchmarkId); checkIsActive(errorMsg); try { return clusterContext.getTaskContexts().submit(descriptor, benchmarkId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public final String submitBenchmark(final TaskDescriptor benchmarkTaskDescriptor) throws BeenApiException { final String groupId = benchmarkTaskDescriptor.getGroupId(); final String bpkId = benchmarkTaskDescriptor.getBpkId(); final String version = benchmarkTaskDescriptor.getVersion(); final String errorMsg = String.format("Failed to submit benchmark descriptor '%s:%s:%s'", groupId, bpkId, version); checkIsActive(errorMsg); try { return clusterContext.getBenchmarks().submit(benchmarkTaskDescriptor); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void killTask(final String taskId) throws BeenApiException { final String errorMsg = String.format("Failed to kill task '%s'", taskId); checkIsActive(errorMsg); try { clusterContext.getTasks().kill(taskId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void killTaskContext(final String taskContextId) throws BeenApiException { final String errorMsg = String.format("Failed to kill task context '%s'", taskContextId); checkIsActive(errorMsg); try { clusterContext.getTaskContexts().kill(taskContextId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void killBenchmark(final String benchmarkId) throws BeenApiException { final String errorMsg = String.format("Failed to kill benchmark '%s'", benchmarkId); checkIsActive(errorMsg); try { clusterContext.getBenchmarks().kill(benchmarkId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void disallowResubmitsForBenchmark(String benchmarkId) throws BeenApiException { final String errorMsg = String.format("Failed to disallow resubmits for benchmark '%s'", benchmarkId); checkIsActive(errorMsg); try { clusterContext.getBenchmarks().disallowResubmits(benchmarkId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void removeTaskEntry(final String taskId) throws BeenApiException { final String errorMsg = String.format("Failed to remove task entry '%s'", taskId); checkIsActive(errorMsg); TaskEntry task = getTask(taskId); try { clusterContext.getTasks().remove(taskId); clearPersistenceForTask(taskId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } // we don't want to remove benchmarks context id if (task.getTaskContextId().equals(Names.BENCHMARKS_CONTEXT_ID)) { return; } // check if task was last not null task of the context in the map try { TaskContextEntry taskContext = getTaskContext(task.getTaskContextId()); List<String> containedTask = taskContext.getContainedTask(); if (containedTask.size() == 1) { // just one task in task context removeTaskContextEntry(task.getTaskContextId()); clusterContext.getTaskContexts().remove(task.getTaskContextId()); } else { // more tasks in task context boolean removable = true; for (String containedTaskId : containedTask) { if (getTask(containedTaskId) != null) { removable = false; } } if (removable) { clusterContext.getTaskContexts().remove(task.getTaskContextId()); clearPersistenceForContext(task.getTaskContextId()); } } } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void removeTaskContextEntry(final String taskContextId) throws BeenApiException { final String errorMsg = String.format("Failed to remove task context entry '%s'", taskContextId); checkIsActive(errorMsg); try { TaskContextEntry taskContextEntry = getTaskContext(taskContextId); clusterContext.getTaskContexts().remove(taskContextId); clearPersistenceForContext(taskContextId); for (String taskId : taskContextEntry.getContainedTask()) { clearPersistenceForTask(taskId); } } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public void removeBenchmarkEntry(final String benchmarkId) throws BeenApiException { final String errorMsg = String.format("Failed to remove benchmark entry '%s'", benchmarkId); checkIsActive(errorMsg); try { BenchmarkEntry benchmarkEntry = getBenchmark(benchmarkId); clusterContext.getBenchmarks().remove(benchmarkId); // cleanup persistence clearPersistenceForTask(benchmarkEntry.getGeneratorId()); clearPersistenceForBenchmark(benchmarkId); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public CommandEntry deleteTaskWrkDirectory(final String runtimeId, final String taskWrkDir) throws BeenApiException { // FIXME needs refactoring // FIXME do not return CommandEntry, but return void and throw exception on error or timeout final String errorMsg = String.format( "Failed to delete task working directory '%s' on runtime '%s'", taskWrkDir, runtimeId); try { long operationId = clusterContext.generateId(DeleteTaskWrkDirMessage.OPERATION_ID_KEY); final DeleteTaskWrkDirMessage deleteMessage = new DeleteTaskWrkDirMessage(runtimeId, taskWrkDir, operationId); clusterContext.getTopics().publishInGlobalTopic(deleteMessage); final IMap<Long, CommandEntry> map = clusterContext.getMap(Names.BEEN_MAP_COMMAND_ENTRIES); final BlockingQueue<CommandEntry> queue = new LinkedBlockingQueue<>(); final EntryListener<Long, CommandEntry> waiter = new CommandEntryMapWaiter(queue); map.addEntryListener(waiter, operationId, true); CommandEntry commandEntry = map.get(operationId); if (commandEntry == null || commandEntry.getState() == CommandEntryState.PENDING) { try { commandEntry = queue.poll(30, TimeUnit.SECONDS); } catch (InterruptedException e) { log.warn("CommandEntry poll interrupted", e); } } map.removeEntryListener(waiter); queue.clear(); if (commandEntry == null) { throw new CommandTimeoutException(String.format( "delete task working directory command " + "with parameters [runtimeId : '%s', taskWrkDirName : '%s'] timeouted", runtimeId, taskWrkDir)); } return commandEntry; } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<DebugListItem> getDebugWaitingTasks() throws BeenApiException { final String errorMsg = "Failed to list tasks waiting for debug"; checkIsActive(errorMsg); try { return new DebugAssistant(clusterContext).listWaitingProcesses(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Map<String, TaskDescriptor> getTaskDescriptors(final BpkIdentifier bpkIdentifier) throws BeenApiException { final String groupId = bpkIdentifier.getGroupId(); final String bpkId = bpkIdentifier.getBpkId(); final String version = bpkIdentifier.getVersion(); final String errorMsg = String.format("Failed to list task descriptors for bpk '%s:%s:%s'", groupId, bpkId, version); checkIsActive(errorMsg); final SwRepoClient client = getSwRepoClient(errorMsg); try { return client.listTaskDescriptors(bpkIdentifier); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public TaskDescriptor getTaskDescriptor(final BpkIdentifier bpkIdentifier, final String descriptorName) throws BeenApiException { final String groupId = bpkIdentifier.getGroupId(); final String bpkId = bpkIdentifier.getBpkId(); final String version = bpkIdentifier.getVersion(); final String errorMsg = String.format( "Failed to get task descriptor for bpk '%s:%s:%s' with name '%s'", groupId, bpkId, version, descriptorName); // check if cluster is live is done in getTaskDescriptors method try { return getTaskDescriptors(bpkIdentifier).get(descriptorName); } catch (BeenApiException e) { throw createBeenApiException(errorMsg, e); } } @Override public Map<String, TaskContextDescriptor> getTaskContextDescriptors(final BpkIdentifier bpkIdentifier) throws BeenApiException { final String groupId = bpkIdentifier.getGroupId(); final String bpkId = bpkIdentifier.getBpkId(); final String version = bpkIdentifier.getVersion(); final String errorMsg = String.format( "Failed to list task context descriptors for bpk '%s:%s:%s'", groupId, bpkId, version); checkIsActive(errorMsg); final SwRepoClient client = getSwRepoClient(errorMsg); try { return client.listTaskContextDescriptors(bpkIdentifier); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public TaskContextDescriptor getTaskContextDescriptor(final BpkIdentifier bpkIdentifier, final String descriptorName) throws BeenApiException { final String groupId = bpkIdentifier.getGroupId(); final String bpkId = bpkIdentifier.getBpkId(); final String version = bpkIdentifier.getVersion(); final String errorMsg = String.format( "Failed to get task context descriptor for bpk '%s:%s:%s' with name '%s'", groupId, bpkId, version, descriptorName); // check if cluster is live is done in getTaskContextDescriptors method try { return getTaskContextDescriptors(bpkIdentifier).get(descriptorName); } catch (BeenApiException e) { throw createBeenApiException(errorMsg, e); } } /** * Performs a general query (e.g. fetch, delete, ...) in the persistence layer * and returns a {@link QueryAnswer} object that represent the query result * and the returned data (if there are any). * * @param query * the query to execute * @return the query answer * @throws BeenApiException * in case of an internal exception, see {@link BeenApi} for * discussion */ private QueryAnswer queryPersistence(final Query query) throws BeenApiException { return performQuery(query, "Failed to perform persistence query"); } @Override public Collection<CommandEntry> listCommandEntries(final String runtimeId) throws BeenApiException { final String errorMsg = String.format("Failed to list command entries for runtime '%s'", runtimeId); checkIsActive(errorMsg); final SqlPredicate predicate = new SqlPredicate(String.format("runtimeId = '%s'", runtimeId)); try { return queryHazelcastMap(Names.BEEN_MAP_COMMAND_ENTRIES, predicate); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskEntry> listActiveTasks(final String runtimeId) throws BeenApiException { final String errorMsg = String.format("Failed to list active tasks on runtime '%s'", runtimeId); checkIsActive(errorMsg); final SqlPredicate predicate = new SqlPredicate(String.format( "runtimeId = '%s' AND state != %s", runtimeId, TaskState.ABORTED)); try { return queryHazelcastMap(Names.TASKS_MAP_NAME, predicate); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } @Override public Collection<TaskEntry> listTasks(final String runtimeId) throws BeenApiException { final String errorMsg = String.format("Failed to list tasks on runtime '%s'", runtimeId); checkIsActive(errorMsg); final SqlPredicate predicate = new SqlPredicate(String.format("runtimeId = '%s'", runtimeId)); try { return queryHazelcastMap(Names.TASKS_MAP_NAME, predicate); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } /** * Performs a general Hazelcast predicate query on the specified map. This * evaluates the predicate in a distributed manner and returns the map entries * that matched the query (for which the predicate returned true). * * @param mapName * the name of the map to query * @param queryPredicate * the query to execute * @param <T> * type of map entries * @return the collection of matching map entries */ private <T> Collection<T> queryHazelcastMap(final String mapName, final SqlPredicate queryPredicate) { final IMap<?, T> map = clusterContext.getMap(mapName); return map.values(queryPredicate); } @Override public Collection<ServiceLogMessage> getServiceLogsByBeenId(final String participantId) throws BeenApiException { final String errorMsg = String.format( "Failed to list service logs for service with participant id '%s'", participantId); final Query query = new QueryBuilder().on(Entities.LOG_SERVICE.getId()).with("beenId", participantId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { return unpackDataAnswer(query, answer, ServiceLogMessage.class); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Collection<ServiceLogMessage> getServiceLogsByHostRuntimeId(final String hostRuntimeId) throws BeenApiException { final String errorMsg = String.format("Failed to list host runtime logs for runtime with id '%s'", hostRuntimeId); final Query query = new QueryBuilder().on(Entities.LOG_SERVICE.getId()).with("hostRuntimeId", hostRuntimeId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { return unpackDataAnswer(query, answer, ServiceLogMessage.class); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Collection<ServiceLogMessage> getServiceLogsByServiceName(final String serviceName) throws BeenApiException { final String errorMsg = String.format("Failed to list logs for service '%s'", serviceName); final Query query = new QueryBuilder().on(Entities.LOG_SERVICE.getId()).with("serviceName", serviceName).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { return unpackDataAnswer(query, answer, ServiceLogMessage.class); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Collection<ServiceLogMessage> getServiceLogsByDate(final Date date) throws BeenApiException { final String errorMsg = String.format( "Failed to list service log messages for date '%s'", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); final Calendar c = Calendar.getInstance(); c.setTime(date); final Long timeToday = c.getTimeInMillis(); c.add(Calendar.DATE, 1); final Long timeTomorrow = c.getTimeInMillis(); final Query query = new QueryBuilder().on(Entities.LOG_SERVICE.getId()).with("created").between( timeToday, timeTomorrow).fetch(); final QueryAnswer answer = queryPersistence(query); try { return unpackDataAnswer(query, answer, ServiceLogMessage.class); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } /** * Checks whether the current cluster context is still active and connected. * If yes, this method simply returns, if no, it throws a * {@link ClusterConnectionUnavailableException} exception with the specified * exception message. * * @param format * the format of the exception message * @param args * arguments of the exception message * @throws ClusterConnectionUnavailableException * when the connection is unavailable */ private void checkIsActive(final String format, final String... args) throws ClusterConnectionUnavailableException { final String message = String.format(format, args); if (!isConnected()) { throw new ClusterConnectionUnavailableException(message); } } @Override public boolean isConnected() { return clusterContext.isActive(); } // TASK STATE RETRIEVAL @Override public Collection<String> getTasksWithState(final TaskState state) throws BeenApiException { final String errorMsg = String.format("Failed to list tasks with state '%s'", state.name()); final Query query = new QueryBuilder().on(Entities.OUTCOME_TASK.getId()).with("taskState", state.name()).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { final Collection<PersistentTaskState> persistentTaskStates = unpackDataAnswer( query, answer, PersistentTaskState.class); final Collection<String> taskIds = new ArrayList<>(persistentTaskStates.size()); for (PersistentTaskState persistentTaskState : persistentTaskStates) { taskIds.add(persistentTaskState.getTaskId()); } return taskIds; } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Collection<String> getTasksWithStateFromContext(final TaskState state, final String contextId) throws BeenApiException { final String errorMsg = String.format( "Failed to list tasks with state '%s' from task context '%s'", state.name(), contextId); final Query query = new QueryBuilder().on(Entities.OUTCOME_TASK.getId()).with("taskState", state.name()).with( "contextId", contextId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); final Collection<PersistentTaskState> persistentTaskStates; try { persistentTaskStates = unpackDataAnswer(query, answer, PersistentTaskState.class); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } final Collection<String> taskIds = new ArrayList<>(persistentTaskStates.size()); for (PersistentTaskState persistentTaskState : persistentTaskStates) { taskIds.add(persistentTaskState.getTaskId()); } return taskIds; } @Override public Collection<String> getTasksWithStateFromBenchmark(TaskState state, final String benchmarkId) throws BeenApiException { final String errorMsg = String.format( "Failed to list tasks with state '%s' from benchmark '%s'", state.name(), benchmarkId); final Query query = new QueryBuilder().on(Entities.OUTCOME_TASK.getId()).with("taskState", state.name()).with( "benchmarkId", benchmarkId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { final Collection<PersistentTaskState> persistentTaskStates = unpackDataAnswer( query, answer, PersistentTaskState.class); final Collection<String> taskIds = new ArrayList<>(persistentTaskStates.size()); for (PersistentTaskState persistentTaskState : persistentTaskStates) { taskIds.add(persistentTaskState.getTaskId()); } return taskIds; } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public TaskState getTaskState(final String taskId) throws BeenApiException { final String errorMsg = String.format("Failed to get state of task '%s'", taskId); final Query query = new QueryBuilder().on(Entities.OUTCOME_TASK.getId()).with("taskId", taskId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { final Collection<PersistentTaskState> states = unpackDataAnswer(query, answer, PersistentTaskState.class); if (states.size() != 1) { throw createBeenApiException( errorMsg, String.format("Found '%d' results but expected exactly 1 result", states.size())); } return states.iterator().next().getTaskState(); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Map<String, TaskState> getTaskStatesForContext(final String contextId) throws BeenApiException { final String errorMsg = String.format("Failed to list states of tasks running in task context '%s'", contextId); final Query query = new QueryBuilder().on(Entities.OUTCOME_TASK.getId()).with("contextId", contextId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { final Collection<PersistentTaskState> states = unpackDataAnswer(query, answer, PersistentTaskState.class); final Map<String, TaskState> statesMap = new HashMap<>(states.size()); for (PersistentTaskState pState : states) { statesMap.put(pState.getTaskId(), pState.getTaskState()); } return statesMap; } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } @Override public Map<String, TaskState> getTaskStatesForBenchmark(final String benchmarkId) throws BeenApiException { final String errorMsg = String.format("Failed to list states of tasks running in benchmark '%s'", benchmarkId); final Query query = new QueryBuilder().on(Entities.OUTCOME_TASK.getId()).with("benchmarkId", benchmarkId).fetch(); final QueryAnswer answer = performQuery(query, errorMsg); try { final Collection<PersistentTaskState> states = unpackDataAnswer(query, answer, PersistentTaskState.class); final Map<String, TaskState> statesMap = new HashMap<>(states.size()); for (PersistentTaskState pState : states) { statesMap.put(pState.getTaskId(), pState.getTaskState()); } return statesMap; } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } } // -------------------- // PERSISTENCE CLEARING // -------------------- @Override public void clearPersistenceForTask(final String taskId) throws BeenApiException { final Query query = new QueryBuilder().with("taskId", taskId).delete(); final String errorMsg = String.format("Failed to delete leftover entities for task with id '%s'", taskId); performQuery(query, errorMsg); } @Override public void clearPersistenceForContext(final String contextId) throws BeenApiException { final Query query = new QueryBuilder().with("contextId", contextId).delete(); final String errorMsg = String.format("Failed to delete leftover entities for task context with id '%s'", contextId); performQuery(query, errorMsg); } @Override public void clearPersistenceForBenchmark(final String benchmarkId) throws BeenApiException { final Query query = new QueryBuilder().with("benchmarkId", benchmarkId).delete(); final String errorMsg = String.format("Failed to delete leftover entities for benchmark with id '%s'", benchmarkId); performQuery(query, errorMsg); } // --------------- // NON-API METHODS // --------------- /** * Unmarshall a collection of accordingly typed entities from a * {@link QueryAnswer}, or throw a {@link DAOException} if something goes * wrong Call this on a {@link QueryAnswer} that should be bringing you data. * Provide the {@link Query} this answer originated from. If the answer is not * carrying the data it should, an informative exception is thrown. * * @param query * {@link Query} you used to retrieve data. This method should only * be using for fetch-type queries * @param answer * The {@link QueryAnswer} you got as a response to this query * @param entityClass * Class of the entity to unmarshall * @param <T> * the type of the entity * @return unpacked answer * @throws DAOException * When there is no data in the answer */ private <T extends Entity> Collection<T> unpackDataAnswer(final Query query, final QueryAnswer answer, final Class<T> entityClass) throws DAOException { if (!answer.isCarryingData()) { throw new DAOException(String.format( "Answer for query '%s' returned with no data: %s", query.toString(), answer.getStatus().getDescription())); } try { return jsonUtils.deserialize(answer.getData(), entityClass); } catch (JsonException e) { throw new DAOException(String.format("Failed to unmarshall data responding to query '%s'", query.toString()), e); } } /** * Perform given query operation * * @param query * query to be performed * @param errorMsg * if the operation fails, message will be added to produced * exception * @throws ClusterConnectionUnavailableException * when been api is not connected to cluster * @throws PersistenceException * when retrieved query answer is invalid or with status other than * OK * @return query answer * @throws BeenApiException * when something other goes wrong while retrieving query answer */ private QueryAnswer performQuery(final Query query, final String errorMsg) throws BeenApiException { checkIsActive(errorMsg); final QueryAnswer answer; try { answer = clusterContext.getPersistence().query(query); } catch (DAOException e) { throw createPersistenceException(errorMsg, e); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } if (!answer.getStatus().isOk()) { throw createPersistenceException(errorMsg, answer.getStatus().getDescription()); } return answer; } /** * Creates and returns an instance of {@link SwRepoClient} that serves as a * client for the software repository. The repository must be running, or else * a {@link BeenApiException} is thrown with the specified message. * * @param errorMsg * the message of the exception * @return a client for the software repository * @throws BeenApiException * when the software repository is unavailable or another internal * exception occurs */ private SwRepoClient getSwRepoClient(final String errorMsg) throws BeenApiException { final ServiceInfo swInfo; try { swInfo = clusterContext.getServices().getSWRepositoryInfo(); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } if (swInfo == null) { throw createSoftwareRepositoryUnavailableException(errorMsg); } try { final SoftwareStore softwareCache = SoftwareStoreBuilderFactory.getSoftwareStoreBuilder().buildCache(); final SwRepoClientFactory swRepoClientFactory = new SwRepoClientFactory(softwareCache); final String hostname = (String) swInfo.getParam(SWRepositoryServiceInfoConstants.PARAM_HOST_NAME); final int port = (int) swInfo.getParam(SWRepositoryServiceInfoConstants.PARAM_PORT); return swRepoClientFactory.getClient(hostname, port); } catch (Exception e) { throw createBeenApiException(errorMsg, e); } } /** * Creates a persistence exception with the specified message and cause. * * @param errorMsg * message of the exception * @param cause * cause of the exception * @return a new exception object */ private PersistenceException createPersistenceException(final String errorMsg, Exception cause) { final String msg = String.format("%s. Reason: %s", errorMsg, cause.getMessage()); return new PersistenceException(msg, cause); } /** * Creates a persistence exception with the specified message and reason. * * @param errorMsg * message of the exception * @param reason * reason of the exception * @return a new exception object */ private PersistenceException createPersistenceException(final String errorMsg, final String reason) { final String msg = String.format("%s. Reason: %s", errorMsg, reason); return new PersistenceException(msg); } /** * Creates an exception which signals that the software repository is * unavailable. * * @param errorMsg * message of the exception * @return a new exception object */ private SoftwareRepositoryUnavailableException createSoftwareRepositoryUnavailableException(final String errorMsg) { final String msg = String.format("%s. Reason: Software repository is not available", errorMsg); return new SoftwareRepositoryUnavailableException(msg); } /** * Creates a general BEEN exception with the specified message and cause. * * @param errorMsg * message of the exception * @param e * cause of the exception * @return a new exception object */ private BeenApiException createBeenApiException(final String errorMsg, final Exception e) { return new BeenApiException(String.format("%s. Reason: %s", errorMsg, e.getMessage()), e); } /** * Creates a persistence exception with the specified message and reason. * * @param errorMsg * message of the exception * @param reason * reason of the exception * @return a new exception object */ private BeenApiException createBeenApiException(final String errorMsg, final String reason) { return new BeenApiException(String.format("%s. Reason: %s", errorMsg, reason)); } /** * Creates a string representation of the BPK identifier and name of a * descriptor. * * @param bpkId * BPK identifier * @param name * name of the descriptor * @return string representation of the parameters */ private String createNamedDescriptorKey(BpkIdentifier bpkId, String name) { return String.format("%s_%s_%s__%s", bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion(), name); } }