/**
* Copyright 2016 Nikita Koksharov
*
* 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.redisson.mapreduce;
import java.io.Serializable;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.redisson.api.RExecutorService;
import org.redisson.api.RFuture;
import org.redisson.api.RScheduledExecutorService;
import org.redisson.api.RedissonClient;
import org.redisson.api.annotation.RInject;
import org.redisson.api.mapreduce.RCollator;
import org.redisson.api.mapreduce.RReducer;
import org.redisson.client.codec.Codec;
/**
*
* @author Nikita Koksharov
*
* @param <KOut> output key
* @param <VOut> output value
*/
public class CoordinatorTask<KOut, VOut> implements Callable<Object>, Serializable {
private static final long serialVersionUID = 7559371478909848610L;
@RInject
protected RedissonClient redisson;
private BaseMapperTask<KOut, VOut> mapperTask;
private RCollator<KOut, VOut, Object> collator;
private RReducer<KOut, VOut> reducer;
protected String objectName;
protected Class<?> objectClass;
private Class<?> objectCodecClass;
private String resultMapName;
private long timeout;
private long startTime;
protected Codec codec;
public CoordinatorTask() {
}
public CoordinatorTask(BaseMapperTask<KOut, VOut> mapperTask, RReducer<KOut, VOut> reducer,
String mapName, String resultMapName, Class<?> mapCodecClass, Class<?> objectClass,
RCollator<KOut, VOut, Object> collator, long timeout, long startTime) {
super();
this.mapperTask = mapperTask;
this.reducer = reducer;
this.objectName = mapName;
this.objectCodecClass = mapCodecClass;
this.objectClass = objectClass;
this.resultMapName = resultMapName;
this.collator = collator;
this.timeout = timeout;
this.startTime = startTime;
}
@Override
public Object call() throws Exception {
long timeSpent = System.currentTimeMillis() - startTime;
if (isTimeoutExpired(timeSpent)) {
throw new MapReduceTimeoutException();
}
this.codec = (Codec) objectCodecClass.getConstructor().newInstance();
RScheduledExecutorService executor = redisson.getExecutorService(RExecutorService.MAPREDUCE_NAME);
int workersAmount = executor.countActiveWorkers();
UUID id = UUID.randomUUID();
String collectorMapName = objectName + ":collector:" + id;
mapperTask.setCollectorMapName(collectorMapName);
mapperTask.setWorkersAmount(workersAmount);
timeSpent = System.currentTimeMillis() - startTime;
if (isTimeoutExpired(timeSpent)) {
throw new MapReduceTimeoutException();
}
if (timeout > 0) {
mapperTask.setTimeout(timeout - timeSpent);
}
mapperTask.addObjectName(objectName);
RFuture<?> mapperFuture = executor.submitAsync(mapperTask);
try {
if (timeout > 0 && !mapperFuture.await(timeout - timeSpent)) {
mapperFuture.cancel(true);
throw new MapReduceTimeoutException();
}
if (timeout == 0) {
mapperFuture.await();
}
} catch (InterruptedException e) {
mapperFuture.cancel(true);
return null;
}
SubTasksExecutor reduceExecutor = new SubTasksExecutor(executor, workersAmount, startTime, timeout);
for (int i = 0; i < workersAmount; i++) {
String name = collectorMapName + ":" + i;
Runnable runnable = new ReducerTask<KOut, VOut>(name, reducer, objectCodecClass, resultMapName, timeout - timeSpent);
reduceExecutor.submit(runnable);
}
if (!reduceExecutor.await()) {
return null;
}
return executeCollator();
}
private Object executeCollator() throws ExecutionException, Exception {
if (collator == null) {
if (timeout > 0) {
redisson.getMap(resultMapName).clearExpire();
}
return null;
}
Callable<Object> collatorTask = new CollatorTask<KOut, VOut, Object>(redisson, collator, resultMapName, objectCodecClass);
long timeSpent = System.currentTimeMillis() - startTime;
if (isTimeoutExpired(timeSpent)) {
throw new MapReduceTimeoutException();
}
if (timeout > 0) {
java.util.concurrent.Future<?> collatorFuture = redisson.getConfig().getExecutor().submit(collatorTask);
try {
return collatorFuture.get(timeout - timeSpent, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return null;
} catch (TimeoutException e) {
collatorFuture.cancel(true);
throw new MapReduceTimeoutException();
}
} else {
return collatorTask.call();
}
}
private boolean isTimeoutExpired(long timeSpent) {
return timeSpent > timeout && timeout > 0;
}
}