/* * Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved * http://www.griddynamics.com * * This library is free software; you can redistribute it and/or modify it under the terms of * the Apache License; either * version 2.0 of the License, or any later version. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.griddynamics.jagger.coordinator.memory; import com.google.common.base.Predicate; import com.google.common.base.Throwables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.griddynamics.jagger.coordinator.*; import com.griddynamics.jagger.coordinator.async.AsyncCallback; import com.griddynamics.jagger.util.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.io.Serializable; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; /** * Performs in memory coordination. <b>Works only for the local mode</b>. * * @author Alexey Kiselyov */ public class MemoryCoordinator implements Coordinator { private static final Logger log = LoggerFactory.getLogger(MemoryCoordinator.class); private Map<NodeId, Pair<NodeContext, Set<CommandExecutor<?, ?>>>> nodes = Maps.newConcurrentMap(); private ExecutorService executorService; private static class InstanceHolder { private static MemoryCoordinator instance = new MemoryCoordinator(); } private MemoryCoordinator() { } public static MemoryCoordinator getInstance() { return InstanceHolder.instance; } @Override public void registerNode(NodeContext nodeContext, Set<Worker> workers, final StatusChangeListener listener) { log.info("Going to register node {} with {} workers", nodeContext.getId(), workers.size()); Set<CommandExecutor<?, ?>> executors = Sets.newHashSet(); Set<Qualifier<?>> qualifiers = Sets.newHashSet(); for (Worker worker : workers) { for (CommandExecutor<?, ?> executor : worker.getExecutors()) { Qualifier<?> qualifier = executor.getQualifier(); if (qualifiers.contains(qualifier)) { throw new CoordinatorException("Executor for qualifier " + qualifier + " is already registered"); } executors.add(executor); } } nodes.put(nodeContext.getId(), Pair.of(nodeContext, executors)); } @Override public RemoteExecutor getExecutor(final NodeId nodeId) throws CoordinatorException { final Pair<NodeContext, Set<CommandExecutor<?, ?>>> nodePair = nodes.get(nodeId); if (nodePair == null) { throw new CoordinatorException("Node " + nodeId.getIdentifier() + " hasn't registered"); } return new AbstractRemoteExecutor() { @Override public <C extends Command<R>, R extends Serializable> void run(final C command, final NodeCommandExecutionListener<C> listener, final AsyncCallback<R> callback) { final NodeContext nodeContext = nodePair.getFirst(); executorService.execute(new Runnable() { @Override public void run() { listener.onCommandExecutionStarted(command, nodeContext); try { R execute = getCommandExecutor(command).execute(command, nodeContext); listener.onCommandExecuted(command); callback.onSuccess(execute); } catch (Throwable throwable) { log.error("Error during command execution!", throwable); callback.onFailure(throwable); } } }); return; } //create Future instance, which can interrupt coordinator thread by some reason(f.e. by time) @Override public <C extends Command<R>, R extends Serializable> Future<R> run(final C command, final NodeCommandExecutionListener<C> listener) { final NodeContext nodeContext = nodePair.getFirst(); return executorService.submit(new Callable<R>() { @Override public R call() { listener.onCommandExecutionStarted(command, nodeContext); R execute = getCommandExecutor(command).execute(command, nodeContext); listener.onCommandExecuted(command); return execute; } }); } private <C extends Command<R>, R extends Serializable> CommandExecutor<C, R> getCommandExecutor(C command){ for (CommandExecutor<?, ?> commandExecutor : nodePair.getSecond()) { final CommandExecutor<C, R> executor = (CommandExecutor<C, R>) commandExecutor; if (executor.getQualifier().equals(Qualifier.of(command))) { return executor; } } throw new CoordinatorException("Command " + command + " is not available on " + nodeId.getIdentifier()); } }; } @Override public boolean canExecuteCommands(NodeId nodeId, Set<Qualifier<?>> qualifiers) throws CoordinatorException { if (qualifiers == null || qualifiers.isEmpty()) { return true; } Set<CommandExecutor<?, ?>> commandExecutors = nodes.get(nodeId).getSecond(); if (commandExecutors == null) throw new CoordinatorException("Node with id " + nodeId + " is not found"); Set<Qualifier<?>> qualifiersCopy = Sets.newHashSet(qualifiers); for (CommandExecutor<?, ?> commandExecutor : commandExecutors) { if (qualifiersCopy.remove(commandExecutor.getQualifier()) && qualifiersCopy.isEmpty()) { return true; } } return false; } @Override public Set<NodeId> getAvailableNodes(final NodeType type) throws CoordinatorException { return Sets.filter(nodes.keySet(), new Predicate<NodeId>() { @Override public boolean apply(NodeId input) { return input.getType().equals(type); } }); } @Override public void waitForReady() { // ready!!! } @Override public void initialize() { // init! } @Override public void waitForInitialization() { // ready! } @Required public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } }