Mit einem _ Long running task _ können in Confluence Aktionen ausgeführt werden, die länger dauern, als ein einzelner HTTP Request.

Typischerweise sind das administrative Massenaktionen, wie zum Beispiel mehrere Seiten innerhalb einer Aktion zu importieren, zu exportieren, anzulegen oder zu löschen.

Confluence Komponenten

Alle nötigen _ Interfaces _ befinden sich im Package com.atlassian.confluence.util.longrunning

Da die Dokumentation von Atlassian hier eher mau ist, ist es am Besten sich an den bestehenden _ Confluence Actions _ zu orientieren. Eine Beispielaktion ist die _ Export Space Action _.

Layout

Die _ Task Action _ kann unter mehreren URLs aufgerufen werden, die das angezeigte Layout der Task Ausführung beeinflussen.

Task innerhalb des Confluence Admin Layout

http://localhost/confluence/admin/scandio-sample-task/doscandiosampletask.action?key=test

longrunningtask_admin

Task innerhalb des Confluence Space oder Page Layout

http://localhost/confluence/doscandiosampletask.action?key=test
http://localhost/confluence/spaces/doscandiosampletask.action?key=test
http://localhost/confluence/pages/doscandiosampletask.action?key=test

longrunningtask_spaces

Task ohne Layout

http://localhost/confluence/plugins/scandio-sample-task/doscandiosampletask.action?key=test

longrunningtask_plugin

Wird die _ Action _ über diesen Link aufgerufen, muss das komplette Layout inklusive _ Decorator  _ von Hand aufgebaut werden. Siehe Creating your Plugin Descriptor

Prototypischer Aufbau eines Long Running Task

Die Beispielaktion ScandioSampleTaskAction ist auf Basis einer _ Space Action _ aufgebaut. Für den _ Task _ ist ein component-import des TransactionTemplate notwendig.

<atlassian-plugin>
	<xwork name="Scandio sample task actions" key="scandio-sample-task-actions">
		<description>Description of the scandio sample long running task actions</description>
		<package name=scandio-sample-task-actions" extends="default">
			<default-interceptor-ref name="validatingStack"/>
			<action name="scandiosampletask" class="de.scandio.confluence.plugins.admin.actions.ScandioSampleTaskAction" method="doDefault">
				<interceptor-ref name="defaultStack" />
				<result name="input" type="velocity">/templates/scandio-sampletask-form.vm</result>
			</action>
			<action name="doscandiosampletask" class="de.scandio.confluence.plugins.admin.actions.ScandioSampleTaskAction" method="doScandioSampleTask">
				<param name="RequireSecurityToken">true</param>
				<result name="input" type="velocity">/templates/scandio-sampletask-form.vm</result>
				<result name="error" type="velocity">/templates/scandio-sampletask-form.vm</result>
				<result name="success" type="dispatcher">/longrunningtask.action?taskId=${taskId}</result>
				<result name="cancel" type="redirect">/plugins/${project.artifactId}/scandiosampletask.action?key=${key}</result>
			</action>
		</package>
	</xwork>
	<component-import key="transactionTemplate" interface="com.atlassian.sal.api.transaction.TransactionTemplate" />
</atlassian-plugin>

Der Aufruf der _ Long running task action  kann durch einen _GET oder einen POST Request erfolgen. Im Beispiel verwenden wir ein Formular, dessen einziges Feld der Security Token ist. Werden weitere Parameter benötigt, können diese über zusätzliche Formularfelder an die Zieladresse übermittelt werden.

#set($key=$req.getParameter("key"))
<form method="POST" class="aui top-label" name="scandiosampletask" action="$req.contextPath/doscandiosampletask.action?key=${key}">
	#form_xsrfToken()
	<input class="button submit" type="submit" name="confirm" value="Run scandio sample task">
</form>

Die Zielklasse der _ Long running task action _ erzeugt in der Methode doScandioSampleTask eine Instanz des _ Long running task _ und übergibt diese an den longRunningTaskManager.

import com.atlassian.confluence.spaces.actions.AbstractSpaceAction;
import com.atlassian.confluence.user.ConfluenceUser;
import com.atlassian.confluence.util.longrunning.LongRunningTaskId;
import com.atlassian.confluence.util.longrunning.LongRunningTaskManager;
import com.atlassian.sal.api.transaction.TransactionTemplate;
public class ScandioSampleTaskAction extends AbstractSpaceAction
{
	private LongRunningTaskId taskId;
	private LongRunningTaskManager longRunningTaskManager;
	public String doScandioSampleTask() throws Exception {
		ConfluenceUser remoteUser = getAuthenticatedUser();
		ScandioSampleTask scandioSampleTask = new ScandioSampleTask(
			remoteUser,
			transactionTemplate
		);
		taskId = longRunningTaskManager.startLongRunningTask(remoteUser, scandioSampleTask);
		return SUCCESS;
	}
	public String getTaskId(){
		return taskId.toString();
	}
	public void setLongRunningTaskManager(LongRunningTaskManager longRunningTaskManager){
		this.longRunningTaskManager = longRunningTaskManager;
	}
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}
}

Die eigentlich Logik des Task wird innerhalb der Methode doInTransaction der Klasse ScandioSampleTaskTransactionCallback implementiert. Beispielhaft ist hier eine for Schleife implementiert, um auch Nutzung des ProgressMeter zu zeigen.

import com.atlassian.confluence.user.AuthenticatedUserThreadLocal;
import com.atlassian.confluence.util.longrunning.ConfluenceAbstractLongRunningTask;
import com.atlassian.sal.api.transaction.TransactionCallback;
import com.atlassian.sal.api.transaction.TransactionTemplate;
public class ScandioSampleTask extends ConfluenceAbstractLongRunningTask
{
	protected final ConfluenceUser user;
	protected TransactionTemplate transactionTemplate;
	public boolean nonBackgroundTask;
	public ScandioSampleTask(
		ConfluenceUser user, TransactionTemplate transactionTemplate)
	{
		this.user = user;
		this.transactionTemplate = transactionTemplate;
	}
	@Override
	protected void runInternal() {
		AuthenticatedUserThreadLocal.set(user);
		Integer status = 0;
		ScandioSampleTaskTransactionCallback<Integer> tc = new ScandioSampleTaskTransactionCallback<Integer>(status);
		transactionTemplate.execute(tc);
	}
	private class ScandioSampleTaskTransactionCallback<T> implements TransactionCallback<T> {
		private T t;
		public ScandioSampleTaskTransactionCallback(T t) {
			this.t = t;
		}
		public T doInTransaction() {
			try {
				progress.setStatus("Start long running task...);
				// ...
				// do something, that takes a long time
				for (int i = 0; i < 1000; i++) {
					progress.setCurrentCount(i + 1);
					progress.setPercentage(i + 1, 1000);
				// ...
				progress.setStatus("Task execution completed.");
				progress.setPercentage(100);
				progress.setCompletedSuccessfully(true);
				return t;
			}
			catch (Exception e) {
				log.error("Error during task execution", e);
				progress.setStatus("There was an error in the content creation. Please check your log files. :" + e.getMessage());
				progress.setCompletedSuccessfully(false);
				throw new RuntimeException("error executing task", e);
			}
		}
	}
	@Override
	public String getName()	{
		return "scandio.sample.task.name";
	}
}