package eu.europeana.cloud.service.dps.storm;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import eu.europeana.cloud.common.model.dps.States;
import eu.europeana.cloud.common.model.dps.TaskState;
import eu.europeana.cloud.service.dps.DpsTask;
import eu.europeana.cloud.service.dps.PluginParameterKeys;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Tuple;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* This bolt is responsible for convert {@link DpsTask} to {@link StormTaskTuple} and it emits result to specific storm stream.
*
* @author Pavel Kefurt <Pavel.Kefurt@gmail.com>
*/
public class ParseTaskBolt extends BaseRichBolt {
protected Map stormConfig;
protected TopologyContext topologyContext;
protected OutputCollector outputCollector;
private static final Logger LOGGER = LoggerFactory.getLogger(ParseTaskBolt.class);
public static final String NOTIFICATION_STREAM_NAME = AbstractDpsBolt.NOTIFICATION_STREAM_NAME;
public final Map<String, String> routingRules;
/**
* Constructor for ParseTaskBolt with routing.
* Task is dropped if TaskName is not in routingRules.
*
* @param routingRules routing table in the form ("TaskName": "StreamName")
*/
public ParseTaskBolt(Map<String, String> routingRules) {
this.routingRules = routingRules;
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
if (routingRules != null) {
for (Map.Entry<String, String> rule : routingRules.entrySet()) {
declarer.declareStream(rule.getValue(), StormTaskTuple.getFields());
}
} else {
declarer.declare(StormTaskTuple.getFields());
}
declarer.declareStream(NOTIFICATION_STREAM_NAME, NotificationTuple.getFields());
}
@Override
public void execute(Tuple tuple) {
ObjectMapper mapper = new ObjectMapper();
DpsTask task;
try {
task = mapper.readValue(tuple.getString(0), DpsTask.class);
} catch (IOException e) {
LOGGER.error("Message '{}' rejected because: {}", tuple.getString(0), e.getMessage());
outputCollector.ack(tuple);
return;
}
Map<String, String> taskParameters = task.getParameters();
String authenticationHeader = taskParameters.get(PluginParameterKeys.AUTHORIZATION_HEADER);
if (authenticationHeader == null) {
LOGGER.error("Message '{}' rejected because: {}", tuple.getString(0), "missing authentication Header");
endTask(task.getTaskId(), "missing authentication Header", TaskState.DROPPED, new Date());
outputCollector.ack(tuple);
return;
}
Date startTime = new Date();
StormTaskTuple stormTaskTuple = new StormTaskTuple(
task.getTaskId(),
task.getTaskName(),
null, null, taskParameters);
String stream = getStream(task);
if (stream != null) {
String dataEntry = convertListToString(task.getDataEntry(stream));
stormTaskTuple.addParameter(PluginParameterKeys.DPS_TASK_INPUT_DATA, dataEntry);
updateTask(task.getTaskId(), "", TaskState.CURRENTLY_PROCESSING, startTime);
outputCollector.emit(stream, tuple, stormTaskTuple.toStormTuple());
} else {
String message = "The taskType is not recognised!";
LOGGER.warn(message);
emitDropNotification(task.getTaskId(), "", message,
taskParameters != null ? taskParameters.toString() : "");
endTask(task.getTaskId(), message, TaskState.DROPPED, new Date());
}
outputCollector.ack(tuple);
}
private void emitDropNotification(long taskId, String resource,
String message, String additionalInformations) {
NotificationTuple nt = NotificationTuple.prepareNotification(taskId,
resource, States.DROPPED, message, additionalInformations);
outputCollector.emit(NOTIFICATION_STREAM_NAME, nt.toStormTuple());
}
private void updateTask(long taskId, String info, TaskState state, Date startTime) {
NotificationTuple nt = NotificationTuple.prepareUpdateTask(taskId, info, state, startTime);
outputCollector.emit(NOTIFICATION_STREAM_NAME, nt.toStormTuple());
}
private void endTask(long taskId, String info, TaskState state, Date finishTime) {
NotificationTuple nt = NotificationTuple.prepareEndTask(taskId, info, state, finishTime);
outputCollector.emit(NOTIFICATION_STREAM_NAME, nt.toStormTuple());
}
private String getStream(DpsTask task) {
if (task.getInputData().get(DpsTask.FILE_URLS) != null)
return DpsTask.FILE_URLS;
else if (task.getInputData().get(DpsTask.DATASET_URLS) != null)
return DpsTask.DATASET_URLS;
return null;
}
private String convertListToString(List<String> list) {
String listString = list.toString();
return listString.substring(1, listString.length() - 1);
}
@Override
public void prepare(Map stormConfig, TopologyContext tc, OutputCollector oc) {
this.stormConfig = stormConfig;
this.topologyContext = tc;
this.outputCollector = oc;
}
}