/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 de.tudarmstadt.ukp.csniper.webapp.support.task;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* <p>
* A Task is some kind of long running action that should be done in the background. The task can
* update the progress based on the current position in the workload.
* </p>
*
* <p>
* Basically what you have to do for your own Task is to override the run() method and frequently
* call one of the updateProgress methods.
* </p>
*
* @author Christopher Hlubek (hlubek)
*
*/
public class Task
{
private Log log = LogFactory.getLog(getClass());
/**
* <p>
* Message is a value object for messages during task runtime.
* </p>
*
* <p>
* These messages could be transferred to the wicket user interface later.
* </p>
*
*/
public static class Message
{
/**
* The severity of the message
*/
public static enum Severity
{
INFO, WARN, ERROR;
}
public final Message.Severity severity;
public final String messageKey;
public final Object[] arguments;
/**
* @param severity
* the severity of the message
* @param messageKey
* key of a message resources
* @param arguments
* these arguments will be used for resource messages
*/
public Message(Message.Severity severity, String messageKey, Object[] arguments)
{
this.severity = severity;
this.messageKey = messageKey;
this.arguments = arguments;
}
/**
* Message with no arguments
*/
public Message(Message.Severity severity, String messageKey)
{
this(severity, messageKey, null);
}
}
private Runnable runnable;
private int current = 0;
private int total = 0;
private boolean done = false;
private boolean cancel = false;
private List<Message> messages = new ArrayList<Message>();
public Task()
{
}
public Task(Runnable runnable)
{
this.runnable = runnable;
}
final Runnable getRunnable()
{
return new Runnable()
{
@Override
public void run()
{
try
{
if (runnable != null)
{
runnable.run();
}
else
{
Task.this.run();
}
}
catch (TaskCanceledException e) {
cancel();
}
catch (Throwable t)
{
// TODO catch Exception and mark task as errorneous
throw new RuntimeException(t);
}
finish();
}
};
}
private void finish()
{
done = true;
doFinish();
}
/**
* <p>
* Implement the actual calculation of the task here.
* </p>
*
*
* <p>
* If iterating and cancelling should be supported you should check for a canceled task in every
* iteration:
* </p>
*
* <pre>
* if (isCancelled())
* {
* return;
* }
* </pre>
*
*/
protected void run()
{
}
/**
* Will be called after finishing the task
*/
protected void doFinish()
{
}
public boolean isCancelled()
{
return cancel;
}
public void checkCanceled()
{
if (cancel) {
throw new TaskCanceledException();
}
}
public void reset()
{
current = 0;
total = 0;
done = false;
cancel = false;
}
public void cancel()
{
log.info("Task canceled.");
cancel = true;
}
/**
* Update the current progress with current / total values (e.g. 1st of 5).
*
* This should be called from the run method for every iteration.
*
* @param aCurrent
* The current iteration (counted from zero!)
* @param aTotal
* Total iterations
*/
public void updateProgress(int aCurrent, int aTotal)
{
current = aCurrent;
total = aTotal;
//progress = (int)Math.ceil(((current + 1) / (double)total) * 100.0);
}
public void setCurrent(int aCurrent)
{
current = aCurrent;
}
public void setTotal(int aTotal)
{
total = aTotal;
}
// /**
// * Update progress with percentage value
// *
// * @param progressPercent
// * progress in percent in [0, 100]
// */
// public void updateProgress(int progressPercent)
// {
// progress = Math.max(progress, progressPercent);
// }
public boolean isDone()
{
return done;
}
public int getProgress()
{
if (total <= 0) {
return 0;
}
else {
return (int)Math.ceil(((current + 1) / (double)total) * 100.0);
}
}
/**
* Add an info message
*/
public void info(String messageKey, Object... arguments)
{
addMessage(messageKey, Message.Severity.INFO, arguments);
}
/**
* Add an warn message
*/
public void warn(String messageKey, Object... arguments)
{
addMessage(messageKey, Message.Severity.WARN, arguments);
}
/**
* Add an error message
*/
public void error(String messageKey, Object... arguments)
{
addMessage(messageKey, Message.Severity.ERROR, arguments);
}
private void addMessage(String messageKey, Message.Severity severity, Object... arguments)
{
messages.add(new Message(severity, messageKey, arguments.length == 0 ? null : arguments));
}
/**
* Get generated messages
*
* @return generated messages
*/
public List<Message> getMessages()
{
return messages;
}
public void increment()
{
current++;
}
public int getCurrent()
{
return current;
}
public int getTotal()
{
return total;
}
}