You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by gi...@apache.org on 2015/05/29 11:50:43 UTC

syncope git commit: [SYNCOPE-660] Added console utilities for runtime task management

Repository: syncope
Updated Branches:
  refs/heads/1_2_X f672ce492 -> 790704c08


[SYNCOPE-660] Added console utilities for runtime task management


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/790704c0
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/790704c0
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/790704c0

Branch: refs/heads/1_2_X
Commit: 790704c08790f8a2536af84ed46290f1596851f0
Parents: f672ce4
Author: giacomolm <gi...@hotmail.it>
Authored: Thu May 28 17:27:12 2015 +0200
Committer: giacomolm <gi...@hotmail.it>
Committed: Fri May 29 10:30:57 2015 +0200

----------------------------------------------------------------------
 .../apache/syncope/console/pages/Reports.java   |  11 ++
 .../org/apache/syncope/console/pages/Tasks.java |  15 ++-
 .../console/pages/panels/NotificationTasks.java |   4 +
 .../console/pages/panels/RuntimePanel.java      | 105 +++++++++++++++++++
 .../console/pages/panels/SchedTasks.java        |   6 ++
 .../console/pages/panels/SyncTasksPanel.java    |   4 +
 .../syncope/console/rest/JobRestClient.java     |  31 ++++++
 .../syncope/console/rest/ReportRestClient.java  |  25 ++++-
 .../syncope/console/rest/TaskRestClient.java    |  23 +++-
 .../html/repeater/data/table/JobColumn.java     |  94 +++++++++++++++++
 .../apache/syncope/console/pages/Reports.html   |  13 +++
 .../org/apache/syncope/console/pages/Tasks.html |  15 +++
 .../console/pages/panels/RuntimePanel.html      |  49 +++++++++
 .../rest/controller/AbstractJobController.java  |  15 ++-
 14 files changed, 404 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/pages/Reports.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/Reports.java b/console/src/main/java/org/apache/syncope/console/pages/Reports.java
index 3f09762..88fdf1c 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/Reports.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/Reports.java
@@ -38,6 +38,7 @@ import org.apache.syncope.console.rest.LoggerRestClient;
 import org.apache.syncope.console.wicket.ajax.markup.html.ClearIndicatingAjaxLink;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.JobColumn;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLinksPanel;
 import org.apache.wicket.Component;
@@ -61,6 +62,7 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
 import org.apache.wicket.model.util.ListModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.spring.injection.annot.SpringBean;
@@ -123,6 +125,8 @@ public class Reports extends BasePage {
         columns.add(new DatePropertyColumn<ReportTO>(new ResourceModel("endDate"), "endDate", "endDate"));
         columns.add(new PropertyColumn<ReportTO, String>(
                 new ResourceModel("latestExecStatus"), "latestExecStatus", "latestExecStatus"));
+        columns.add(new JobColumn<ReportTO, String>(new StringResourceModel("", this, null, ""), "runtime",
+                getPageReference(), reportRestClient));
         columns.add(new ActionColumn<ReportTO, String>(new ResourceModel("actions", "")) {
 
             private static final long serialVersionUID = 2054811145491901166L;
@@ -411,4 +415,11 @@ public class Reports extends BasePage {
             };
         }
     }
+    /**
+     * IndicatorMarkupId behaviour is embedded in Reports.html
+     */
+    @Override
+    public String getAjaxIndicatorMarkupId() {
+        return "";
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/pages/Tasks.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/Tasks.java b/console/src/main/java/org/apache/syncope/console/pages/Tasks.java
index aae3622..11819c6 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/Tasks.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/Tasks.java
@@ -152,10 +152,10 @@ public class Tasks extends BasePage {
         @Override
         public Iterator<T> iterator(final long first, final long count) {
             final List<T> tasks = new ArrayList<T>();
-            
+
             final int page = ((int) first / paginatorRows);
-            
-            for (T task : restClient.list(reference, (page < 0 ? 0 : page)  + 1, paginatorRows, getSort())) {
+
+            for (T task : restClient.list(reference, (page < 0 ? 0 : page) + 1, paginatorRows, getSort())) {
                 if (task instanceof SchedTaskTO && ((SchedTaskTO) task).getLastExec() == null
                         && task.getExecutions() != null && !task.getExecutions().isEmpty()) {
 
@@ -227,4 +227,13 @@ public class Tasks extends BasePage {
 
         return table;
     }
+
+    /**
+     * IndicatorMarkupId behaviour is embedded in Tasks.html
+     */
+    @Override
+    public String getAjaxIndicatorMarkupId() {
+        return "";
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/pages/panels/NotificationTasks.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/panels/NotificationTasks.java b/console/src/main/java/org/apache/syncope/console/pages/panels/NotificationTasks.java
index 86fd7bc..9c7b458 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/panels/NotificationTasks.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/panels/NotificationTasks.java
@@ -28,6 +28,7 @@ import org.apache.syncope.console.pages.NotificationTaskModalPage;
 import org.apache.syncope.console.pages.Tasks;
 import org.apache.syncope.console.pages.Tasks.TasksProvider;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.JobColumn;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLinksPanel;
 import org.apache.wicket.Component;
@@ -152,6 +153,9 @@ public class NotificationTasks extends AbstractTasks {
         columns.add(new PropertyColumn<AbstractTaskTO, String>(
                 new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus"));
 
+        columns.add(new JobColumn<AbstractTaskTO, String>(new StringResourceModel("", this, null, ""), "runtime",
+                pageRef, restClient)); 
+
         columns.add(new ActionColumn<AbstractTaskTO, String>(new StringResourceModel("actions", this, null, "")) {
 
             private static final long serialVersionUID = 2054811145491901166L;

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/pages/panels/RuntimePanel.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/panels/RuntimePanel.java b/console/src/main/java/org/apache/syncope/console/pages/panels/RuntimePanel.java
new file mode 100644
index 0000000..3bc525c
--- /dev/null
+++ b/console/src/main/java/org/apache/syncope/console/pages/panels/RuntimePanel.java
@@ -0,0 +1,105 @@
+/*
+ * 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 org.apache.syncope.console.pages.panels;
+
+import org.apache.syncope.console.rest.JobRestClient;
+import org.apache.syncope.console.wicket.ajax.markup.html.ClearIndicatingAjaxLink;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.markup.html.panel.Panel;
+
+public class RuntimePanel extends Panel {
+
+    private static final long serialVersionUID = -9002724127542172464L;
+
+    private boolean latestStatus;
+
+    private Fragment fragmentStop, fragmentSpinner;
+
+    public AbstractAjaxTimerBehavior timer;
+
+    private final PageReference pageRef;
+
+    private final long jobId;
+
+    private final JobRestClient jobRestClient;
+
+    public RuntimePanel(final String componentId, final IModel<?> model, final PageReference pageRef, final long jobId,
+            final JobRestClient jobRestClient) {
+        super(componentId, model);
+        this.pageRef = pageRef;
+        this.jobId = jobId;
+        this.jobRestClient = jobRestClient;
+        latestStatus = false;
+        this.refresh();
+
+    }
+
+    public final void refresh() {
+        boolean currentStatus = jobRestClient.isJobRunning(jobId);
+        if (currentStatus && !latestStatus) {
+            setRunning();
+        } else if (!currentStatus) {
+            setNotRunning();
+        }
+        latestStatus = currentStatus;
+    }
+
+    public void setRunning() {
+        fragmentStop = new Fragment("panelStop", "fragmentStop", this);
+        fragmentStop.addOrReplace(new ClearIndicatingAjaxLink<Void>("stopLink", pageRef) {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            protected void onClickInternal(final AjaxRequestTarget target) {
+                jobRestClient.stopJob(jobId);
+                this.setEnabled(false);
+                target.add(this);
+            }
+
+            @Override
+            public String getAjaxIndicatorMarkupId() {
+                return "";
+            }
+        });
+        addOrReplace(fragmentStop);
+        fragmentSpinner = new Fragment("panelSpinner", "fragmentSpinner", this);
+        addOrReplace(fragmentSpinner);
+    }
+
+    public void setNotRunning() {
+        fragmentStop = new Fragment("panelStop", "emptyFragment", this);
+        addOrReplace(fragmentStop);
+        fragmentSpinner = new Fragment("panelSpinner", "emptyFragment", this);
+        addOrReplace(fragmentSpinner);
+    }
+
+    public void setTimer(AbstractAjaxTimerBehavior timer) {
+        if (this.timer != null) {
+            remove(this.timer);
+        }
+        this.timer = timer;
+        this.add(this.timer);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/pages/panels/SchedTasks.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/panels/SchedTasks.java b/console/src/main/java/org/apache/syncope/console/pages/panels/SchedTasks.java
index f092e55..78591a8 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/panels/SchedTasks.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/panels/SchedTasks.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.console.pages.panels;
 
+import static org.apache.syncope.console.pages.panels.AbstractTasks.TASKS;
+
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.syncope.common.to.SchedTaskTO;
@@ -30,6 +32,7 @@ import org.apache.syncope.console.pages.Tasks.TasksProvider;
 import org.apache.syncope.console.wicket.ajax.markup.html.ClearIndicatingAjaxLink;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.JobColumn;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLinksPanel;
 import org.apache.wicket.Component;
@@ -167,6 +170,9 @@ public class SchedTasks extends AbstractTasks {
         columns.add(new PropertyColumn<AbstractTaskTO, String>(
                 new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus"));
 
+        columns.add(new JobColumn<AbstractTaskTO, String>(new StringResourceModel("", this, null, ""), "runtime",
+                pageRef, restClient)); 
+
         columns.add(new ActionColumn<AbstractTaskTO, String>(new StringResourceModel("actions", this, null, "")) {
 
             private static final long serialVersionUID = 2054811145491901166L;

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/pages/panels/SyncTasksPanel.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/panels/SyncTasksPanel.java b/console/src/main/java/org/apache/syncope/console/pages/panels/SyncTasksPanel.java
index 71fd2b5..032f4e4 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/panels/SyncTasksPanel.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/panels/SyncTasksPanel.java
@@ -29,6 +29,7 @@ import org.apache.syncope.console.pages.SyncTaskModalPage;
 import org.apache.syncope.console.pages.UserTemplateModalPage;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table.JobColumn;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.console.wicket.markup.html.form.ActionLinksPanel;
 import org.apache.wicket.Component;
@@ -69,6 +70,9 @@ public class SyncTasksPanel extends AbstractSyncTasksPanel<SyncTaskTO> {
                 new StringResourceModel("nextExec", this, null), "nextExec", "nextExec"));
         syncTaskscolumns.add(new PropertyColumn<AbstractTaskTO, String>(
                 new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus"));
+        
+        syncTaskscolumns.add(new JobColumn<AbstractTaskTO, String>(new StringResourceModel("", this, null, ""), "runtime",
+                pageRef, restClient));        
 
         syncTaskscolumns.add(
                 new ActionColumn<AbstractTaskTO, String>(new StringResourceModel("actions", this, null, "")) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/rest/JobRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/JobRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/JobRestClient.java
new file mode 100644
index 0000000..de361f3
--- /dev/null
+++ b/console/src/main/java/org/apache/syncope/console/rest/JobRestClient.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.apache.syncope.console.rest;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public abstract class JobRestClient extends BaseRestClient{
+
+    public abstract boolean isJobRunning(final long id);
+
+    public abstract void startJob(final long id);
+
+    public abstract void stopJob(final long id);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/rest/ReportRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/ReportRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/ReportRestClient.java
index dff42bf..9b8afdb 100644
--- a/console/src/main/java/org/apache/syncope/console/rest/ReportRestClient.java
+++ b/console/src/main/java/org/apache/syncope/console/rest/ReportRestClient.java
@@ -25,12 +25,15 @@ import org.apache.syncope.common.services.ReportService;
 import org.apache.syncope.common.to.ReportTO;
 import org.apache.syncope.common.types.ReportExecExportFormat;
 import org.apache.syncope.common.SyncopeClientException;
+import org.apache.syncope.common.to.ReportExecTO;
+import org.apache.syncope.common.types.JobAction;
+import org.apache.syncope.common.types.JobStatusType;
 import org.apache.syncope.common.wrap.ReportletConfClass;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Component;
 
 @Component
-public class ReportRestClient extends BaseRestClient implements ExecutionRestClient {
+public class ReportRestClient extends JobRestClient implements ExecutionRestClient {
 
     private static final long serialVersionUID = 1644689667998953604L;
 
@@ -105,4 +108,24 @@ public class ReportRestClient extends BaseRestClient implements ExecutionRestCli
     public Response exportExecutionResult(final Long executionId, final ReportExecExportFormat fmt) {
         return getService(ReportService.class).exportExecutionResult(executionId, fmt);
     }
+
+    @Override
+    public boolean isJobRunning(final long reportId) {
+        for (ReportExecTO reportExecTO : getService(ReportService.class).listJobs(JobStatusType.RUNNING)) {
+            if (reportExecTO.getReport() == reportId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void startJob(final long reportId) {
+        getService(ReportService.class).actionJob(reportId, JobAction.START);
+    }
+
+    @Override
+    public void stopJob(final long reportId) {
+        getService(ReportService.class).actionJob(reportId, JobAction.STOP);
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/rest/TaskRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/TaskRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/TaskRestClient.java
index 863e51a..c83ce88 100644
--- a/console/src/main/java/org/apache/syncope/console/rest/TaskRestClient.java
+++ b/console/src/main/java/org/apache/syncope/console/rest/TaskRestClient.java
@@ -34,6 +34,9 @@ import org.apache.syncope.common.types.TaskType;
 import org.apache.syncope.common.util.CollectionWrapper;
 import org.apache.syncope.common.SyncopeClientException;
 import org.apache.syncope.common.to.PushTaskTO;
+import org.apache.syncope.common.to.TaskExecTO;
+import org.apache.syncope.common.types.JobAction;
+import org.apache.syncope.common.types.JobStatusType;
 import org.apache.syncope.common.wrap.PushActionClass;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Component;
@@ -42,7 +45,7 @@ import org.springframework.stereotype.Component;
  * Console client for invoking Rest Tasks services.
  */
 @Component
-public class TaskRestClient extends BaseRestClient implements ExecutionRestClient {
+public class TaskRestClient extends JobRestClient implements ExecutionRestClient {
 
     private static final long serialVersionUID = 6284485820911028843L;
 
@@ -167,4 +170,22 @@ public class TaskRestClient extends BaseRestClient implements ExecutionRestClien
     public BulkActionResult bulkAction(final BulkAction action) {
         return getService(TaskService.class).bulk(action);
     }
+    
+    @Override
+    public boolean isJobRunning(final long taskId){
+        for(TaskExecTO taskExecTO : getService(TaskService.class).listJobs(JobStatusType.RUNNING)){
+            if(taskExecTO.getTask()== taskId) return true;
+        }
+        return false;
+    }
+    
+    @Override
+    public void startJob(final long taskId){
+        getService(TaskService.class).actionJob(taskId, JobAction.START);
+    }
+
+    @Override
+    public void stopJob(final long taskId) {
+        getService(TaskService.class).actionJob(taskId, JobAction.STOP);
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/JobColumn.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/JobColumn.java b/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/JobColumn.java
new file mode 100644
index 0000000..563e3cb
--- /dev/null
+++ b/console/src/main/java/org/apache/syncope/console/wicket/extensions/markup/html/repeater/data/table/JobColumn.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.apache.syncope.console.wicket.extensions.markup.html.repeater.data.table;
+
+import org.apache.syncope.common.to.AbstractTaskTO;
+import org.apache.syncope.common.to.ReportTO;
+import org.apache.syncope.console.pages.panels.RuntimePanel;
+import org.apache.syncope.console.rest.JobRestClient;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JobColumn<T, S> extends AbstractColumn<T, S> {
+
+    private static final long serialVersionUID = 7955560320949560725L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(JobColumn.class);
+
+    private final PageReference pageRef;
+
+    private RuntimePanel panel;
+
+    private final JobRestClient jobRestClient;
+
+    public JobColumn(final IModel<String> displayModel, final S sortProperty, final PageReference pageRef,
+            final JobRestClient jobRestClient) {
+        super(displayModel, sortProperty);
+        this.pageRef = pageRef;
+        this.jobRestClient = jobRestClient;
+    }
+
+    @Override
+    public void populateItem(final Item<ICellPopulator<T>> item, final String componentId, final IModel<T> model) {
+        Long jobId = null;
+        if (model.getObject() instanceof AbstractTaskTO) {
+            jobId = ((AbstractTaskTO) model.getObject()).getId();
+        } else if (model.getObject() instanceof ReportTO) {
+            jobId = ((ReportTO) model.getObject()).getId();
+        }
+        if (jobId != null) {
+            panel = new RuntimePanel(componentId, model, pageRef, jobId, jobRestClient);
+            startPolling(10);
+            item.add(panel);
+        }
+    }
+
+    public void startPolling(final int seconds) {
+        AbstractAjaxTimerBehavior timer = new AbstractAjaxTimerBehavior(Duration.seconds(seconds)) {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void onTimer(AjaxRequestTarget target) {
+                panel.refresh();
+                target.add(panel);
+            }
+
+            @Override
+            protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
+                super.updateAjaxAttributes(attributes);
+                attributes.getExtraParameters().put("pollingTimeout", "true");
+            }
+
+        };
+
+        panel.setTimer(timer);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/resources/org/apache/syncope/console/pages/Reports.html
----------------------------------------------------------------------
diff --git a/console/src/main/resources/org/apache/syncope/console/pages/Reports.html b/console/src/main/resources/org/apache/syncope/console/pages/Reports.html
index 456f7a3..ff5fb37 100644
--- a/console/src/main/resources/org/apache/syncope/console/pages/Reports.html
+++ b/console/src/main/resources/org/apache/syncope/console/pages/Reports.html
@@ -18,7 +18,20 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:extend>
+    <script type="text/javascript">
+      window.onload = setupFunc;
 
+      function setupFunc() {
+        Wicket.Event.subscribe('/ajax/call/beforeSend', function (attributes, jqXHR, settings) {
+          if (!jqXHR.ep || !jqXHR.ep[0] || !jqXHR.ep[0]["value"]) {
+            document.getElementById("veil").style.display = "block";
+          }
+        });
+        Wicket.Event.subscribe('/ajax/call/complete', function (attributes, jqXHR, textStatus) {
+          document.getElementById("veil").style.display = "none";
+        });
+      }
+    </script>
     <div id="tabs">
       <ul>
         <li class="tabs-selected"><a href="#tabs-1"><span><wicket:message key="reports"/></span></a></li>

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/resources/org/apache/syncope/console/pages/Tasks.html
----------------------------------------------------------------------
diff --git a/console/src/main/resources/org/apache/syncope/console/pages/Tasks.html b/console/src/main/resources/org/apache/syncope/console/pages/Tasks.html
index 7df418a..81c9237 100644
--- a/console/src/main/resources/org/apache/syncope/console/pages/Tasks.html
+++ b/console/src/main/resources/org/apache/syncope/console/pages/Tasks.html
@@ -18,6 +18,21 @@ under the License.
 -->
 <wicket:extend>
 
+  <script type="text/javascript">
+    window.onload = setupFunc;
+
+    function setupFunc() {
+      Wicket.Event.subscribe('/ajax/call/beforeSend', function (attributes, jqXHR, settings) {
+        if (!jqXHR.ep || !jqXHR.ep[0] || !jqXHR.ep[0]["value"]){
+          document.getElementById("veil").style.display = "block";
+        }
+      });
+      Wicket.Event.subscribe('/ajax/call/complete', function (attributes, jqXHR, textStatus) {
+        document.getElementById("veil").style.display = "none";
+      });
+    }
+  </script>
+
   <div id="tabs">
     <ul>
       <li class="tabs-selected"><a href="#tabs-1"><span><wicket:message key="tab1"/></span></a></li>

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/console/src/main/resources/org/apache/syncope/console/pages/panels/RuntimePanel.html
----------------------------------------------------------------------
diff --git a/console/src/main/resources/org/apache/syncope/console/pages/panels/RuntimePanel.html b/console/src/main/resources/org/apache/syncope/console/pages/panels/RuntimePanel.html
new file mode 100644
index 0000000..828192c
--- /dev/null
+++ b/console/src/main/resources/org/apache/syncope/console/pages/panels/RuntimePanel.html
@@ -0,0 +1,49 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:head>
+    <style>
+      #runtime{
+        text-align: center;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel wicket:id="runtime">
+    <div>
+      <div id="runtime">
+        <div id="runtimePanel">
+          <span wicket:id="panelStop"></span>
+          <span wicket:id="panelSpinner"></span>
+
+          <wicket:fragment wicket:id="fragmentStop">
+            <a href="#" wicket:id="stopLink"><img src="img/actions/suspend.png" alt="stop icon" title="Stop Now"/></a>
+          </wicket:fragment>
+
+          <wicket:fragment wicket:id="fragmentSpinner">
+            <img src="img/loading.gif" alt="spinner icon" title="Spinner"/>
+          </wicket:fragment>
+
+          <wicket:fragment wicket:id="emptyFragment"></wicket:fragment>
+
+        </div>
+
+      </div>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/790704c0/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java
index ca759d9..e7aa31e 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractJobController.java
@@ -158,7 +158,20 @@ abstract class AbstractJobController<T extends AbstractBaseBean> extends Abstrac
                 if (scheduler.getScheduler().checkExists(jobKey)) {
                     switch (action) {
                         case START:
-                            scheduler.getScheduler().triggerJob(jobKey);
+                            Long currentId = getIdFromJobName(jobKey);
+                            boolean found = false;
+                            //Two or more equals jobs cannot be executed concurrently
+                            for (int i = 0; i < scheduler.getScheduler().getCurrentlyExecutingJobs().size() && !found;
+                                    i++) {
+                                JobExecutionContext jec = scheduler.getScheduler().getCurrentlyExecutingJobs().get(i);
+                                Long jobId = getIdFromJobName(jec.getJobDetail().getKey());
+                                if (jobId == currentId) {
+                                    found = true;
+                                }
+                            }
+                            if (!found) {
+                                scheduler.getScheduler().triggerJob(jobKey);
+                            }
                             break;
 
                         case STOP: