/*
* 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;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.griddynamics.jagger.coordinator.Command;
import com.griddynamics.jagger.coordinator.async.AsyncCallback;
import com.griddynamics.jagger.coordinator.async.AsyncRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
public class DefaultPackExchanger implements PackExchanger, AsyncRunner<Command<Serializable>, Serializable> {
private static final Logger log = LoggerFactory.getLogger(DefaultPackExchanger.class);
private final LinkedBlockingQueue<PackEntry<Command<Serializable>>> commandsToSend = new LinkedBlockingQueue<PackEntry<Command<Serializable>>>();
private final LinkedBlockingQueue<PackEntry<Serializable>> resultsToSend = new LinkedBlockingQueue<PackEntry<Serializable>>();
private final Map<UUID, AsyncCallback<Serializable>> expectedResults = Maps.newConcurrentMap();
private final Executor executor;
private final AsyncRunner<Command<Serializable>, Serializable> incomingCommandRunner;
public DefaultPackExchanger(Executor executor, AsyncRunner<Command<Serializable>, Serializable> incomingCommandRunner) {
this.executor = executor;
this.incomingCommandRunner = incomingCommandRunner;
}
public LinkedBlockingQueue<PackEntry<Command<Serializable>>> getCommandsToSend() {
return commandsToSend;
}
public LinkedBlockingQueue<PackEntry<Serializable>> getResultsToSend() {
return resultsToSend;
}
@Override
public Pack retrieve() {
log.debug("Pack retrieving requested");
List<PackEntry<Command<Serializable>>> commandsChunk = Lists.newLinkedList();
commandsToSend.drainTo(commandsChunk);
List<PackEntry<Serializable>> resultsChunk = Lists.newLinkedList();
resultsToSend.drainTo(resultsChunk);
log.debug("{} commands to send; {} results to sends", resultsToSend.size(), commandsToSend.size());
return Pack
.builder()
.addAllCommands(commandsChunk)
.addAllResults(resultsChunk)
.build();
}
@Override
public void process(Pack income) {
log.debug("Exchange of packs requested");
processResults(income.getResults());
scheduleCommands(income.getCommands());
}
private void scheduleCommands(List<PackEntry<Command<Serializable>>> commands) {
log.debug("Scheduling commands requested");
for (PackEntry<Command<Serializable>> entry : commands) {
final UUID id = entry.getId();
final Command<Serializable> command = entry.getValue();
log.debug("Going to schedule command {} with id {}", command, id);
incomingCommandRunner.run(command, new AsyncCallback<Serializable>() {
@Override
public void onSuccess(Serializable result) {
log.debug("Command {} with id {} executed successfully", command, id);
resultsToSend.add(PackEntry.create(id, result));
}
@Override
public void onFailure(Throwable throwable) {
log.debug("Command {} with id {} execution failed", command, id);
resultsToSend.add(PackEntry.fail(id, throwable));
}
});
}
log.debug("Scheduling commands completed");
}
private void processResults(List<PackEntry<Serializable>> results) {
log.debug("Processing results requested");
for (PackEntry<Serializable> entry : results) {
final UUID id = entry.getId();
final Serializable result = entry.getValue();
final Throwable error = entry.getException();
log.debug("Processing result for command with id {}. Result : {}", id, result);
final AsyncCallback<Serializable> callback = expectedResults.get(id);
if (callback == null) {
throw new IllegalStateException("No callback for command with id " + id + " found");
}
expectedResults.remove(id);
if (error == null) {
executor.execute(new Runnable() {
@Override
public void run() {
log.debug("Calling onSuccess for command {}", id);
callback.onSuccess(result);
log.debug("onSuccess for command {} called", id);
}
});
} else {
executor.execute(new Runnable() {
@Override
public void run() {
log.debug("Calling onFailure for command {}", id);
callback.onFailure(error);
log.debug("onFailure for command {} called", id);
}
});
}
}
log.debug("Processing results completed");
}
@Override
public void run(Command<Serializable> command, AsyncCallback<Serializable> callback) {
log.debug("Going to schedule command {} for next pack", command);
UUID uuid = UUID.randomUUID();
expectedResults.put(uuid, callback);
PackEntry<Command<Serializable>> entry = PackEntry.create(uuid, command);
commandsToSend.add(entry);
log.debug("Pack entry {} is scheduled for client", entry);
}
public void clean() {
commandsToSend.clear();
resultsToSend.clear();
expectedResults.clear();
log.debug("Pack exchanger cleaned");
}
}