/*
* 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.http.server;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.SettableFuture;
import com.griddynamics.jagger.coordinator.*;
import com.griddynamics.jagger.coordinator.async.AsyncCallback;
import com.griddynamics.jagger.coordinator.async.AsyncRunner;
import com.griddynamics.jagger.coordinator.async.FutureAsyncCallback;
import com.griddynamics.jagger.coordinator.http.AbstractProxyWorker;
import com.griddynamics.jagger.coordinator.http.DefaultPackExchanger;
import com.griddynamics.jagger.coordinator.http.PackExchanger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
/**
* Adapts {@link Coordinator} to {@link TransportHandler}.
*
* @author Mairbek Khadikov
*/
public class CoordinatorAdapter implements TransportHandler {
private static final Logger log = LoggerFactory.getLogger(CoordinatorAdapter.class);
private static final StatusChangeListener LOGGING_LISTENER = new StatusChangeListener() {
@Override
public void onNodeStatusChanged(NodeId nodeId, NodeStatus status) {
log.debug("node {} status changed {}", nodeId, status);
}
@Override
public void onCoordinatorDisconnected() {
log.warn("Coordinator disconnected");
}
@Override
public void onCoordinatorConnected() {
log.debug("Coordinator connected");
}
};
private final Map<NodeId, DefaultPackExchanger> exchangers = Maps.newConcurrentMap();
private final Coordinator coordinator;
private final Executor executor;
public CoordinatorAdapter(Coordinator coordinator, Executor executor) {
this.coordinator = coordinator;
this.executor = executor;
}
@Override
public PackExchanger getExchanger(NodeId node) {
return exchangers.get(node);
}
@Override
public void registerNode(final NodeId node, Set<Qualifier<Command<Serializable>>> qualifiers) {
log.debug("Register node {} with qualifiers {} requested", node, qualifiers);
Worker worker = createProxyWorker(qualifiers);
coordinator.registerNode(Coordination.emptyContext(node), Sets.newHashSet(worker), LOGGING_LISTENER);
exchangers.put(node, createPackRunner(node));
log.debug("Node {} registered", node);
}
private DefaultPackExchanger createPackRunner(final NodeId node) {
return new DefaultPackExchanger(executor, new AsyncRunner<Command<Serializable>, Serializable>() {
@Override
public void run(Command<Serializable> command, AsyncCallback<Serializable> callback) {
log.debug("Going send command {} to delegate coordinator", command);
RemoteExecutor remoteExecutor = coordinator.getExecutor(node);
remoteExecutor.run(command, Coordination.doNothing(), callback);
}
});
}
private AbstractProxyWorker createProxyWorker(final Set<Qualifier<Command<Serializable>>> qualifiers) {
return new AbstractProxyWorker(qualifiers) {
@Override
protected Serializable handleCommand(Command<Serializable> command, NodeContext nodeContext) {
log.debug("Handling command {} for node {}", command, nodeContext);
AsyncRunner<Command<Serializable>, Serializable> packExchanger = exchangers.get(nodeContext.getId());
FutureAsyncCallback<Serializable> callback = FutureAsyncCallback.create();
packExchanger.run(command, callback);
SettableFuture<Serializable> future = callback.getFuture();
try {
log.debug("Waiting for command completion");
return future.get();
} catch (InterruptedException e) {
throw Throwables.propagate(e);
} catch (ExecutionException e) {
throw Throwables.propagate(e);
}
}
};
}
}