/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.yarn.container;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.yarn.YarnSystemException;
/**
* Default implementation of {@link ContainerHandlersResultsProcessor}.
*
* @author Janne Valkealahti
*
*/
class DefaultContainerHandlersResultsProcessor implements ContainerHandlersResultsProcessor {
private static final Log log = LogFactory.getLog(DefaultContainerHandlersResultsProcessor.class);
private final List<Result> wrappedResults = new ArrayList<Result>();
private final AtomicInteger activeListenables = new AtomicInteger();
private Exception runtimeException = null;
private ListenablesComplete listener;
@Override
public void process(List<Object> results) {
for (Object result : results) {
wrappedResults.add(new Result(result));
if (result instanceof ListenableFuture<?>) {
activeListenables.incrementAndGet();
}
}
for (final Result wrappedResult : wrappedResults) {
if (wrappedResult.result instanceof ListenableFuture<?>) {
((ListenableFuture<?>) wrappedResult.result).addCallback(new ListenableFutureCallback<Object>() {
@Override
public void onSuccess(Object result) {
if (log.isDebugEnabled()) {
log.info("onSuccess for " + wrappedResult + " with result=[" + result + "]");
}
wrappedResult.setResult(result);
activeListenables.decrementAndGet();
mayNotifyListener();
}
@Override
public void onFailure(Throwable t) {
if (log.isDebugEnabled()) {
log.info("onFailure for " + wrappedResult + " with throwable=[" + t + "]");
}
runtimeException = new YarnSystemException("error", t);
activeListenables.decrementAndGet();
mayNotifyListener();
}
});
}
}
}
@Override
public ResultHolder getResult() {
final List<Object> res = new ArrayList<Object>();
for (Result r : wrappedResults) {
try {
res.add(r.getResult());
} catch (Exception e) {
log.debug("Future get() resulted error", e);
}
}
return new ResultHolder() {
@Override
public List<Object> getResults() {
return res;
}
@Override
public Exception getException() {
return runtimeException;
}
};
}
@Override
public void cancel() {
for (final Result wrappedResult : wrappedResults) {
try {
log.info("Cancelling " + wrappedResult);
wrappedResult.cancelIfFuture();
} catch (Exception e) {
log.error("error in cancel", e);
}
}
}
@Override
public boolean isListenablesDone() {
return activeListenables.get() == 0;
}
@Override
public void setListenablesCompleteListener(ListenablesComplete listener) {
this.listener = listener;
}
private void mayNotifyListener() {
if (activeListenables.get() == 0) {
if (listener != null) {
listener.complete();
}
}
}
/**
* Wrapped for result object to make it easier to handle
* result as a {@link Future}.
*/
private static class Result {
Object result;
Result(Object result) {
this.result = result;
}
/**
* Request a cancel if result is a {@link Future}.
*/
void cancelIfFuture() {
if (result instanceof Future<?>) {
((Future<?>)result).cancel(true);
}
}
/**
* Gets the result. If result is a future this
* method delegates to {@link Future#get()}.
*
* @return the result
*/
Object getResult() {
if (result instanceof Future<?>) {
Future<?> f = (Future<?>)result;
try {
return f.get();
} catch (CancellationException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
return new YarnSystemException("Future throwed error", e.getCause());
}
return null;
} else {
return result;
}
}
/**
* Sets the result.
*
* @param result the new result
*/
void setResult(Object result) {
this.result = result;
}
}
}