package transparent.core;
import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import transparent.core.database.Database;
enum TaskType {
PRODUCT_LIST_PARSE,
PRODUCT_INFO_PARSE,
IMAGE_FETCH
}
public class Task implements Comparable<Task>, Callable<Object>
{
private final TaskType type;
private final Module module;
private ModuleThread wrapper;
private ScheduledFuture<Object> future;
private long time;
private boolean reschedules;
private boolean dummy;
private String state;
/* this id is transient and is regenerated every time the task is loaded */
private int id;
private static AtomicInteger ID_COUNTER = new AtomicInteger(0);
private static ConcurrentHashMap<Integer, Task> tasks =
new ConcurrentHashMap<Integer, Task>();
private int persistentIndex = -1;
private int index = -1;
private boolean running = false;
private boolean stopped = false;
public Task(TaskType type, Module module,
long time, boolean reschedules, boolean dummy, String state)
{
this.type = type;
this.module = module;
this.time = time;
this.reschedules = reschedules;
this.dummy = dummy;
this.id = ID_COUNTER.getAndIncrement();
this.state = ((state == null) ? "" : state);
tasks.put(id, this);
}
public static Task getTask(int id) {
return tasks.get(id);
}
public static Task removeTask(int id) {
Task task = tasks.remove(id);
if (task != null)
task.id = -1;
return task;
}
public TaskType getType() {
return type;
}
public Module getModule() {
return module;
}
public void setFuture(ScheduledFuture<Object> future) {
this.future = future;
}
public ScheduledFuture<Object> getFuture() {
return future;
}
public int getId() {
return id;
}
public long getTime() {
return time;
}
public boolean reschedules() {
return this.reschedules;
}
public boolean isDummy() {
return this.dummy;
}
public int getPersistentIndex() {
return persistentIndex;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public boolean isRunning() {
return this.running;
}
public void setRunning(boolean running) {
this.running = running;
}
public String getState() {
if (wrapper != null)
state = wrapper.getState();
return this.state;
}
private static Task load(String data)
{
String[] tokens = data.split("\\.");
if (tokens.length != 5 && tokens.length != 6) {
Console.printError("Task", "load", "Unable to parse string.");
return null;
}
TaskType type;
if (tokens[0].equals("0"))
type = TaskType.PRODUCT_LIST_PARSE;
else if (tokens[0].equals("1"))
type = TaskType.PRODUCT_INFO_PARSE;
else if (tokens[0].equals("2"))
type = TaskType.IMAGE_FETCH;
else {
Console.printError("Task", "load", "Unable to parse task type.");
return null;
}
long id = new BigInteger(tokens[1]).longValue();
long time = new BigInteger(tokens[2]).longValue();
boolean reschedules = !tokens[3].equals("0");
boolean dummy = !tokens[4].equals("0");
Task task = new Task(type, Core.getModule(id), time, reschedules, dummy, null);
if (tokens.length > 5)
task.state = unescape(tokens[5]);
return task;
}
public static Task load(Database database, String queue, int index)
{
String job = database.getMetadata(queue + "." + index);
Task task = Task.load(job);
if (task == null) {
Console.printError("Task", "load",
"Unable to parse task at index " + index + ".");
return null;
}
task.persistentIndex = index;
task.index = index;
return task;
}
private String save()
{
String typeString;
switch (type) {
case PRODUCT_LIST_PARSE:
typeString = "0";
break;
case PRODUCT_INFO_PARSE:
typeString = "1";
break;
case IMAGE_FETCH:
typeString = "2";
break;
default:
Console.printError("Task", "save", "Unrecognized type.");
return null;
}
String reschedulesString = reschedules ? "1" : "0";
String dummyString = dummy ? "1" : "0";
return typeString + "." + module.getIdString() + "." + time
+ "." + reschedulesString + "." + dummyString + "." + escape(state);
}
public boolean save(Database database, boolean isRunning, int index)
{
String queue = Core.getQueueName(isRunning);
if (wrapper != null)
state = wrapper.getState();
String data = save();
if (data != null && database.setMetadata(queue + "." + index, data)) {
this.persistentIndex = index;
this.index = index;
return true;
}
return false;
}
@Override
public int compareTo(Task o) {
if (time < o.time)
return -1;
else if (time > o.time)
return 1;
else return 0;
}
public void stop(boolean cancelRescheduling) {
if (wrapper != null)
wrapper.stop();
if (cancelRescheduling)
this.stopped = true;
}
public boolean stopped() {
return this.stopped;
}
@Override
public Object call() throws Exception
{
try {
/* notify the core that this task has started */
Core.startTask(this);
switch (type) {
case PRODUCT_LIST_PARSE:
wrapper = new ModuleThread(module, dummy);
wrapper.setState(state);
wrapper.setRequestType(Core.PRODUCT_LIST_REQUEST);
wrapper.run();
Core.stopTask(this, false);
if (reschedules && !stopped) {
Core.queueTask(new Task(TaskType.PRODUCT_INFO_PARSE,
module, System.currentTimeMillis(), true, dummy, null));
if (!Core.saveQueue())
Console.printError("Task", "call", "Unable to save tasks.");
}
return null;
case PRODUCT_INFO_PARSE:
wrapper = new ModuleThread(module, dummy);
wrapper.setState(state);
wrapper.setRequestType(Core.PRODUCT_INFO_REQUEST);
wrapper.setRequestedProductIds(Core.getDatabase().getProductIds(module));
wrapper.run();
Core.stopTask(this, false);
if (reschedules && !stopped) {
Core.queueTask(new Task(TaskType.PRODUCT_LIST_PARSE,
module, System.currentTimeMillis(), true, dummy, null));
if (!Core.saveQueue())
Console.printError("Task", "call", "Unable to save tasks.");
}
return null;
case IMAGE_FETCH:
Core.fetchImages(this, module);
Core.stopTask(this, false);
if (reschedules) {
Core.queueTask(new Task(TaskType.IMAGE_FETCH,
module, System.currentTimeMillis(), true, dummy, null));
if (!Core.saveQueue())
Console.printError("Task", "call", "Unable to save tasks.");
}
return null;
default:
Console.printError("Task", "call", "Unrecognized task type.");
Core.stopTask(this, true);
return null;
}
} catch (Exception e) {
Console.printError("Task", "run", "", e);
return null;
}
}
private static String escape(String s) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
switch (s.charAt(i)) {
case '.':
builder.append("\\d");
break;
case '\\':
builder.append("\\\\");
break;
default:
builder.append(s.charAt(i));
}
}
return builder.toString();
}
private static String unescape(String s) {
StringBuilder builder = new StringBuilder();
boolean escape = false;
for (int i = 0; i < s.length(); i++) {
switch (s.charAt(i)) {
case '\\':
if (escape) {
builder.append('\\');
escape = false;
} else
escape = true;
break;
case 'd':
if (escape) {
builder.append('.');
escape = false;
} else
builder.append(s.charAt(i));
break;
default:
builder.append(s.charAt(i));
}
}
return builder.toString();
}
}