diff --git a/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/BulkProcessingPage.java b/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/BulkProcessingPage.java
index 1378ce1063b..963cada8b19 100644
--- a/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/BulkProcessingPage.java
+++ b/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/BulkProcessingPage.java
@@ -48,7 +48,8 @@ public BulkProcessingPage(PageParameters aParameters)
queue(new BulkRecommenderPanel("processingPanel", getProjectModel()));
- queue(new TaskMonitorPanel("runningProcesses").setPopupMode(false)
+ queue(new TaskMonitorPanel("runningProcesses", getProject()) //
+ .setPopupMode(false) //
.setShowFinishedTasks(true));
}
}
diff --git a/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkPredictionTask.java b/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkPredictionTask.java
index 26041729673..e85760bfaf8 100644
--- a/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkPredictionTask.java
+++ b/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkPredictionTask.java
@@ -54,6 +54,7 @@
import de.tudarmstadt.ukp.inception.recommendation.api.model.Recommender;
import de.tudarmstadt.ukp.inception.recommendation.tasks.PredictionTask;
import de.tudarmstadt.ukp.inception.recommendation.tasks.RecommendationTask_ImplBase;
+import de.tudarmstadt.ukp.inception.scheduling.ProjectTask;
import de.tudarmstadt.ukp.inception.scheduling.SchedulingService;
import de.tudarmstadt.ukp.inception.scheduling.TaskState;
import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService;
@@ -65,6 +66,7 @@
public class BulkPredictionTask
extends RecommendationTask_ImplBase
+ implements ProjectTask
{
public static final String TYPE = "BulkPredictionTask";
diff --git a/inception/inception-project-api/pom.xml b/inception/inception-project-api/pom.xml
index e86306afe7e..c084cb39241 100644
--- a/inception/inception-project-api/pom.xml
+++ b/inception/inception-project-api/pom.xml
@@ -52,14 +52,9 @@
commons-lang3
-
org.springframework
spring-context
-
- org.springframework.security
- spring-security-core
-
\ No newline at end of file
diff --git a/inception/inception-project-api/src/main/java/de/tudarmstadt/ukp/inception/project/api/ProjectService.java b/inception/inception-project-api/src/main/java/de/tudarmstadt/ukp/inception/project/api/ProjectService.java
index c422496f854..18eb89321a4 100644
--- a/inception/inception-project-api/src/main/java/de/tudarmstadt/ukp/inception/project/api/ProjectService.java
+++ b/inception/inception-project-api/src/main/java/de/tudarmstadt/ukp/inception/project/api/ProjectService.java
@@ -31,7 +31,6 @@
import org.apache.commons.lang3.Validate;
import org.slf4j.MDC;
-import org.springframework.security.access.prepost.PreAuthorize;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.PermissionLevel;
@@ -66,7 +65,6 @@ public interface ProjectService
* @deprecated Use {@link #assignRole(Project, User, PermissionLevel...)} instead.
*/
@Deprecated
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER', 'ROLE_REMOTE')")
void createProjectPermission(ProjectPermission aPermission);
/**
@@ -349,7 +347,6 @@ List
listProjectsWithUserHavingRole(User aUser, PermissionLevel aRole,
* @throws IOException
* if the project to be deleted is not available in the file system
*/
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")
void removeProject(Project aProject) throws IOException;
/**
diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/RecommendationEditorExtension.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/RecommendationEditorExtension.java
index d97c5ed22e9..b3584892a78 100644
--- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/RecommendationEditorExtension.java
+++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/RecommendationEditorExtension.java
@@ -34,7 +34,6 @@
import org.apache.uima.cas.CAS;
import org.apache.uima.jcas.tcas.Annotation;
-import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.feedback.IFeedback;
import org.slf4j.Logger;
@@ -63,7 +62,6 @@
import de.tudarmstadt.ukp.inception.recommendation.api.event.AjaxRecommendationRejectedEvent;
import de.tudarmstadt.ukp.inception.recommendation.api.event.PredictionsSwitchedEvent;
import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion;
-import de.tudarmstadt.ukp.inception.recommendation.api.model.Predictions;
import de.tudarmstadt.ukp.inception.recommendation.api.model.RelationSuggestion;
import de.tudarmstadt.ukp.inception.recommendation.api.model.SpanSuggestion;
import de.tudarmstadt.ukp.inception.recommendation.config.RecommenderServiceAutoConfiguration;
@@ -209,10 +207,10 @@ private void actionAcceptPrediction(AnnotationActionHandler aActionHandler,
private Optional getPrediction(AnnotatorState aState, VID aRecVid)
{
- Predictions predictions = recommendationService.getPredictions(aState.getUser(),
+ var predictions = recommendationService.getPredictions(aState.getUser(),
aState.getProject());
- SourceDocument document = aState.getDocument();
- Optional prediction = predictions //
+ var document = aState.getDocument();
+ var prediction = predictions //
.getPredictionByVID(document, aRecVid);
return prediction;
}
@@ -254,7 +252,7 @@ private void actionRejectRecommendation(AnnotationActionHandler aActionHandler,
new AjaxRecommendationRejectedEvent(aTarget, aState, aVID));
// Trigger a re-rendering of the document
- Page page = aTarget.getPage();
+ var page = aTarget.getPage();
page.send(page, BREADTH, new SelectionChangedEvent(aTarget));
}
@@ -264,7 +262,7 @@ public void renderRequested(AjaxRequestTarget aTarget, AnnotatorState aState)
log.trace("renderRequested()");
// do not show predictions during curation or when viewing others' work
- String sessionOwner = userService.getCurrentUsername();
+ var sessionOwner = userService.getCurrentUsername();
if (!aState.getMode().equals(ANNOTATION)) {
return;
}
@@ -273,8 +271,7 @@ public void renderRequested(AjaxRequestTarget aTarget, AnnotatorState aState)
// at the moment. For another, even if we had it, it would be quite annoying to the user
// if the UI kept updating itself without any the user expecting an update. The user does
// expect an update when she makes some interaction, so we piggy-back on this expectation.
- boolean switched = recommendationService.switchPredictions(sessionOwner,
- aState.getProject());
+ var switched = recommendationService.switchPredictions(sessionOwner, aState.getProject());
log.trace("switchPredictions() returned {}", switched);
if (!switched) {
diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/footer/RecommendationEventFooterPanel.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/footer/RecommendationEventFooterPanel.java
index 00db17402ac..f21294fa7a4 100644
--- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/footer/RecommendationEventFooterPanel.java
+++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/footer/RecommendationEventFooterPanel.java
@@ -44,7 +44,7 @@
import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
import de.tudarmstadt.ukp.clarin.webanno.ui.core.page.ProjectPageBase;
import de.tudarmstadt.ukp.inception.support.svelte.SvelteBehavior;
-import de.tudarmstadt.ukp.inception.websocket.feedback.FeedbackPanelExtensionBehavior;
+import de.tudarmstadt.ukp.inception.ui.core.feedback.FeedbackPanelExtensionBehavior;
public class RecommendationEventFooterPanel
extends WebMarkupContainer
diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/render/RecommendationRenderer.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/render/RecommendationRenderer.java
index 662cd589a2c..0c970f7684e 100644
--- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/render/RecommendationRenderer.java
+++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/render/RecommendationRenderer.java
@@ -34,7 +34,6 @@
import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Recommender;
import de.tudarmstadt.ukp.inception.recommendation.config.RecommenderServiceAutoConfiguration;
-import de.tudarmstadt.ukp.inception.rendering.editorstate.AnnotatorState;
import de.tudarmstadt.ukp.inception.rendering.pipeline.RenderStep;
import de.tudarmstadt.ukp.inception.rendering.request.RenderRequest;
import de.tudarmstadt.ukp.inception.rendering.vmodel.VDocument;
@@ -74,7 +73,7 @@ public String getId()
@Override
public boolean accepts(RenderRequest aRequest)
{
- AnnotatorState state = aRequest.getState();
+ var state = aRequest.getState();
if (aRequest.getCas() == null) {
return false;
diff --git a/inception/inception-scheduling/pom.xml b/inception/inception-scheduling/pom.xml
index 2439fe835cb..629b532417c 100644
--- a/inception/inception-scheduling/pom.xml
+++ b/inception/inception-scheduling/pom.xml
@@ -46,6 +46,10 @@
de.tudarmstadt.ukp.inception.app
inception-support
+
+ de.tudarmstadt.ukp.inception.app
+ inception-websocket
+
@@ -64,10 +68,6 @@
org.springframework.boot
spring-boot
-
- org.springframework
- spring-messaging
-
org.springframework.security
spring-security-core
diff --git a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/NotifyingTaskMonitor.java b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/NotifyingTaskMonitor.java
index 93adf3abf34..41c05f9f599 100644
--- a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/NotifyingTaskMonitor.java
+++ b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/NotifyingTaskMonitor.java
@@ -17,25 +17,22 @@
*/
package de.tudarmstadt.ukp.inception.scheduling;
-import static de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerWebsocketController.BASE_TOPIC;
-import static de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerWebsocketController.TASKS_TOPIC;
-
-import org.springframework.messaging.simp.SimpMessagingTemplate;
-
+import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerWebsocketController;
import de.tudarmstadt.ukp.inception.scheduling.controller.model.MTaskStateUpdate;
import de.tudarmstadt.ukp.inception.support.logging.LogMessage;
public class NotifyingTaskMonitor
extends TaskMonitor
{
- private final SimpMessagingTemplate msgTemplate;
+ private final SchedulerWebsocketController schedulerWebsocketController;
private MTaskStateUpdate lastUpdate;
- public NotifyingTaskMonitor(TaskHandle aHandle, Task aTask, SimpMessagingTemplate aMsgTemplate)
+ public NotifyingTaskMonitor(TaskHandle aHandle, Task aTask,
+ SchedulerWebsocketController aSchedulerWebsocketController)
{
super(aHandle, aTask);
- msgTemplate = aMsgTemplate;
+ schedulerWebsocketController = aSchedulerWebsocketController;
}
@Override
@@ -89,7 +86,7 @@ protected void onDestroy()
{
var msg = new MTaskStateUpdate(this, true);
if (getUser() != null) {
- msgTemplate.convertAndSendToUser(getUser(), "/queue" + BASE_TOPIC + TASKS_TOPIC, msg);
+ schedulerWebsocketController.dispatch(msg);
}
}
@@ -102,8 +99,7 @@ private void sendNotification()
var msg = new MTaskStateUpdate(this);
if (lastUpdate == null || !lastUpdate.equals(msg)) {
if (getUser() != null) {
- msgTemplate.convertAndSendToUser(getUser(), "/queue" + BASE_TOPIC + TASKS_TOPIC,
- msg);
+ schedulerWebsocketController.dispatch(msg);
}
lastUpdate = msg;
}
diff --git a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/ProjectTask.java b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/ProjectTask.java
new file mode 100644
index 00000000000..206a8e6a430
--- /dev/null
+++ b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/ProjectTask.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Technische Universität Darmstadt under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The Technische Universität Darmstadt
+ * 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.
+ *
+ * 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.inception.scheduling;
+
+import de.tudarmstadt.ukp.clarin.webanno.model.Project;
+
+/**
+ * Marks a task as belonging to a project. That implies that all project managers can perform
+ * actions on it and that it should be shown on the processing page in the project.
+ */
+public interface ProjectTask
+{
+ Project getProject();
+}
diff --git a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/Task.java b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/Task.java
index b706a443a9b..89f3a6eb1a9 100644
--- a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/Task.java
+++ b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/Task.java
@@ -29,11 +29,11 @@
import org.slf4j.MDC;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.messaging.simp.SimpMessagingTemplate;
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
import de.tudarmstadt.ukp.inception.documents.api.RepositoryProperties;
+import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerWebsocketController;
public abstract class Task
implements Runnable, InitializingBean
@@ -41,7 +41,7 @@ public abstract class Task
private final static AtomicInteger nextId = new AtomicInteger(1);
private @Autowired RepositoryProperties repositoryProperties;
- private @Autowired(required = false) SimpMessagingTemplate msgTemplate;
+ private @Autowired(required = false) SchedulerWebsocketController schedulerController;
private final TaskHandle handle;
private final User sessionOwner;
@@ -80,8 +80,8 @@ public void afterPropertiesSet()
{
// For tasks that have a parent task, we use a non-notifying monitor. Also, we do not report
// such subtasks ia the SchedulerControllerImpl - they are internal.
- if (msgTemplate != null && sessionOwner != null && parentTask == null) {
- monitor = new NotifyingTaskMonitor(handle, this, msgTemplate);
+ if (schedulerController != null && sessionOwner != null && parentTask == null) {
+ monitor = new NotifyingTaskMonitor(handle, this, schedulerController);
}
else {
monitor = new TaskMonitor(handle, this);
diff --git a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/TaskMonitor.java b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/TaskMonitor.java
index d2406ca7a85..26962a7fa0b 100644
--- a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/TaskMonitor.java
+++ b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/TaskMonitor.java
@@ -26,6 +26,7 @@
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
+import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
import de.tudarmstadt.ukp.inception.support.logging.LogMessage;
@@ -34,6 +35,7 @@ public class TaskMonitor
private final Deque messages = new ConcurrentLinkedDeque<>();
private final TaskHandle handle;
+ private final Project project;
private final String user;
private final String title;
private final String type;
@@ -56,6 +58,7 @@ public TaskMonitor(TaskHandle aHandle, Task aTask)
handle = aHandle;
type = aTask.getType();
user = aTask.getUser().map(User::getUsername).orElse(null);
+ project = aTask.getProject();
title = aTask.getTitle();
createTime = System.currentTimeMillis();
cancellable = aTask.isCancellable();
@@ -76,6 +79,11 @@ public String getUser()
return user;
}
+ public Project getProject()
+ {
+ return project;
+ }
+
public String getTitle()
{
return title;
diff --git a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/SchedulerWebsocketController.java b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/SchedulerWebsocketController.java
index 2e2bf1e1f25..4d30425514f 100644
--- a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/SchedulerWebsocketController.java
+++ b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/SchedulerWebsocketController.java
@@ -18,10 +18,43 @@
package de.tudarmstadt.ukp.inception.scheduling.controller;
import static de.tudarmstadt.ukp.inception.security.config.InceptionSecurityWebUIApiAutoConfiguration.BASE_API_URL;
+import static de.tudarmstadt.ukp.inception.websocket.config.WebSocketConstants.PARAM_PROJECT;
+import static de.tudarmstadt.ukp.inception.websocket.config.WebSocketConstants.TOPIC_ELEMENT_PROJECT;
+
+import java.util.Properties;
+
+import org.springframework.util.PropertyPlaceholderHelper;
+
+import de.tudarmstadt.ukp.clarin.webanno.model.Project;
+import de.tudarmstadt.ukp.inception.scheduling.controller.model.MTaskStateUpdate;
public interface SchedulerWebsocketController
{
String BASE_URL = BASE_API_URL + "/scheduler";
+
String BASE_TOPIC = "/scheduler";
- String TASKS_TOPIC = "/tasks";
+ String USER_TASKS_TOPIC = BASE_TOPIC + "/user";
+ String PROJECT_TASKS_TOPIC_TEMPLATE = BASE_TOPIC + TOPIC_ELEMENT_PROJECT + "{" + PARAM_PROJECT
+ + "}";
+
+ void dispatch(MTaskStateUpdate update);
+
+ static String getUserTaskUpdatesTopic()
+ {
+ return USER_TASKS_TOPIC;
+ }
+
+ static String getProjectTaskUpdatesTopic(Project aProject)
+ {
+ return getProjectTaskUpdatesTopic(aProject.getId());
+ }
+
+ static String getProjectTaskUpdatesTopic(long aProjectId)
+ {
+ var properties = new Properties();
+ properties.setProperty(PARAM_PROJECT, String.valueOf(aProjectId));
+ var replacer = new PropertyPlaceholderHelper("{", "}", null, false);
+ var topic = replacer.replacePlaceholders(PROJECT_TASKS_TOPIC_TEMPLATE, properties);
+ return topic;
+ }
}
diff --git a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/model/MTaskStateUpdate.java b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/model/MTaskStateUpdate.java
index b9e6472eaa8..8e6f5721136 100644
--- a/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/model/MTaskStateUpdate.java
+++ b/inception/inception-scheduling/src/main/java/de/tudarmstadt/ukp/inception/scheduling/controller/model/MTaskStateUpdate.java
@@ -29,14 +29,19 @@
public class MTaskStateUpdate
{
- private final long timestamp;
private final int id;
+ private final String title;
+ private final long timestamp;
private final String type;
+
+ private final String username;
+ private final long projectId;
+ private final String projectName;
+
+ private final TaskState state;
+
private final int progress;
private final int maxProgress;
- private final TaskState state;
- private final String title;
- private final int messageCount;
@JsonInclude(Include.NON_DEFAULT)
private final boolean cancellable;
@@ -44,6 +49,8 @@ public class MTaskStateUpdate
@JsonInclude(Include.NON_DEFAULT)
private final boolean removed;
+ private final int messageCount;
+
@JsonInclude(Include.NON_EMPTY)
private final LogMessage latestMessage;
@@ -54,17 +61,41 @@ public MTaskStateUpdate(TaskMonitor aMonitor)
public MTaskStateUpdate(TaskMonitor aMonitor, boolean aRemoved)
{
- timestamp = System.currentTimeMillis();
- title = aMonitor.getTitle();
id = aMonitor.getHandle().getId();
+ title = aMonitor.getTitle();
+ timestamp = System.currentTimeMillis();
type = aMonitor.getType();
+
+ username = aMonitor.getUser();
+
+ projectId = aMonitor.getProject() != null ? aMonitor.getProject().getId() : -1;
+ projectName = aMonitor.getProject() != null ? aMonitor.getProject().getName() : null;
+
+ state = aMonitor.getState();
+
progress = aMonitor.getProgress();
maxProgress = aMonitor.getMaxProgress();
- state = aMonitor.getState();
+ cancellable = aMonitor.isCancellable();
+
messageCount = aMonitor.getMessages().size();
latestMessage = aMonitor.getMessages().peekLast();
+
removed = aRemoved;
- cancellable = aMonitor.isCancellable();
+ }
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public long getProjectId()
+ {
+ return projectId;
+ }
+
+ public String getProjectName()
+ {
+ return projectName;
}
public String getTitle()
diff --git a/inception/inception-schema-api/pom.xml b/inception/inception-schema-api/pom.xml
index 709e56334a2..9f1de19819e 100644
--- a/inception/inception-schema-api/pom.xml
+++ b/inception/inception-schema-api/pom.xml
@@ -64,10 +64,6 @@
org.springframework
spring-context
-
- org.springframework.security
- spring-security-core
-
org.slf4j
diff --git a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/AnnotationSchemaService.java b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/AnnotationSchemaService.java
index 6d56e8bb5cc..65b9fb11be4 100644
--- a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/AnnotationSchemaService.java
+++ b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/AnnotationSchemaService.java
@@ -28,7 +28,6 @@
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.wicket.validation.ValidationError;
-import org.springframework.security.access.prepost.PreAuthorize;
import de.tudarmstadt.ukp.clarin.webanno.api.casstorage.CasUpgradeMode;
import de.tudarmstadt.ukp.clarin.webanno.api.type.CASMetadata;
@@ -64,7 +63,6 @@ public interface AnnotationSchemaService
* @param tag
* the tag.
*/
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")
void createTag(Tag tag);
/**
@@ -74,7 +72,6 @@ public interface AnnotationSchemaService
* @param tag
* the tag.
*/
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")
void createTags(Tag... tag);
void updateTagRanks(TagSet aTagSet, List aTags);
@@ -87,7 +84,6 @@ public interface AnnotationSchemaService
* @param tagset
* the tagset.
*/
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")
void createTagSet(TagSet tagset);
/**
@@ -99,7 +95,6 @@ public interface AnnotationSchemaService
* @param type
* the type.
*/
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")
void createOrUpdateLayer(AnnotationLayer type);
void createFeature(AnnotationFeature feature);
@@ -444,7 +439,6 @@ public interface AnnotationSchemaService
* @param tag
* the tag.
*/
- @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_USER')")
void removeTag(Tag tag);
/**
diff --git a/inception/inception-search-core/src/main/java/de/tudarmstadt/ukp/inception/search/scheduling/tasks/ReindexTask.java b/inception/inception-search-core/src/main/java/de/tudarmstadt/ukp/inception/search/scheduling/tasks/ReindexTask.java
index 6ad28256c6b..e53d6437a1d 100644
--- a/inception/inception-search-core/src/main/java/de/tudarmstadt/ukp/inception/search/scheduling/tasks/ReindexTask.java
+++ b/inception/inception-search-core/src/main/java/de/tudarmstadt/ukp/inception/search/scheduling/tasks/ReindexTask.java
@@ -33,6 +33,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import de.tudarmstadt.ukp.inception.scheduling.MatchResult;
+import de.tudarmstadt.ukp.inception.scheduling.ProjectTask;
import de.tudarmstadt.ukp.inception.scheduling.Task;
import de.tudarmstadt.ukp.inception.search.SearchService;
import de.tudarmstadt.ukp.inception.search.model.Monitor;
@@ -43,6 +44,7 @@
*/
public class ReindexTask
extends IndexingTask_ImplBase
+ implements ProjectTask
{
public static final String TYPE = "ReindexTask";
diff --git a/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/WicketSecurityUtils.java b/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/WicketSecurityUtils.java
new file mode 100644
index 00000000000..12d6eda38bc
--- /dev/null
+++ b/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/WicketSecurityUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Technische Universität Darmstadt under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The Technische Universität Darmstadt
+ * 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.
+ *
+ * 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.clarin.webanno.security;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
+
+public class WicketSecurityUtils
+{
+ public static String getCsrfTokenFromSession()
+ {
+ var httpRequest = (HttpServletRequest) RequestCycle.get().getRequest()
+ .getContainerRequest();
+ var httpResponse = (HttpServletResponse) RequestCycle.get().getResponse()
+ .getContainerResponse();
+
+ var csrfTokenRepository = new HttpSessionCsrfTokenRepository();
+ var csrfToken = csrfTokenRepository.loadDeferredToken(httpRequest, httpResponse);
+
+ if (csrfToken != null) {
+ return csrfToken.get().getToken();
+ }
+ else {
+ return "";
+ }
+ }
+
+ /**
+ * Checks if auto-logout is enabled. For Winstone, we get a max session length of 0, so here it
+ * is disabled.
+ */
+ public static int getAutoLogoutTime()
+ {
+ int duration = 0;
+ var request = RequestCycle.get().getRequest();
+ if (request instanceof ServletWebRequest servletRequest) {
+ var session = servletRequest.getContainerRequest().getSession();
+ if (session != null) {
+ duration = session.getMaxInactiveInterval();
+ }
+ }
+ return duration;
+ }
+}
diff --git a/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/config/InceptionSecurityAutoConfiguration.java b/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/config/InceptionSecurityAutoConfiguration.java
index fd0e7457ed0..9e73a635d73 100644
--- a/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/config/InceptionSecurityAutoConfiguration.java
+++ b/inception/inception-security/src/main/java/de/tudarmstadt/ukp/clarin/webanno/security/config/InceptionSecurityAutoConfiguration.java
@@ -30,6 +30,7 @@
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -42,6 +43,7 @@
import de.tudarmstadt.ukp.inception.support.deployment.DeploymentModeService;
@EnableWebSecurity
+@EnableMethodSecurity
public class InceptionSecurityAutoConfiguration
{
@Bean
diff --git a/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/wicket/WicketUtil.java b/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/wicket/WicketUtil.java
index a7c6c316479..606bc74bc22 100644
--- a/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/wicket/WicketUtil.java
+++ b/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/wicket/WicketUtil.java
@@ -17,6 +17,8 @@
*/
package de.tudarmstadt.ukp.inception.support.wicket;
+import static java.lang.String.format;
+
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
@@ -29,8 +31,10 @@
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Response;
+import org.apache.wicket.request.Url;
import org.apache.wicket.request.cycle.IRequestCycleListener;
import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
import org.apache.wicket.request.cycle.RequestCycle;
@@ -248,4 +252,19 @@ public void onBeginRequest(RequestCycle aCycle)
aApplication.getRequestCycleSettings()
.addResponseFilter(new WicketUtil.TimingResponseFilter());
}
+
+ public static String constructEndpointUrl(String aUrl)
+ {
+ var contextPath = WebApplication.get().getServletContext().getContextPath();
+ var endPointUrl = Url.parse(format("%s%s", contextPath, aUrl));
+ return RequestCycle.get().getUrlRenderer().renderFullUrl(endPointUrl);
+ }
+
+ public static String constructWsEndpointUrl(String aUrl)
+ {
+ var contextPath = WebApplication.get().getServletContext().getContextPath();
+ var endPointUrl = Url.parse(format("%s%s", contextPath, aUrl));
+ endPointUrl.setProtocol("ws");
+ return RequestCycle.get().getUrlRenderer().renderFullUrl(endPointUrl);
+ }
}
diff --git a/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/core/logout/LogoutPanel.java b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/core/logout/LogoutPanel.java
index 936e21052d9..af16e95e771 100644
--- a/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/core/logout/LogoutPanel.java
+++ b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/core/logout/LogoutPanel.java
@@ -17,12 +17,11 @@
*/
package de.tudarmstadt.ukp.clarin.webanno.ui.core.logout;
+import static de.tudarmstadt.ukp.clarin.webanno.security.WicketSecurityUtils.getAutoLogoutTime;
import static de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior.enabledWhen;
import static de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior.visibleWhen;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
-import javax.servlet.http.HttpSession;
-
import org.apache.wicket.Component;
import org.apache.wicket.devutils.stateless.StatelessComponent;
import org.apache.wicket.markup.head.IHeaderResponse;
@@ -33,9 +32,6 @@
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
-import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
-import org.apache.wicket.request.Request;
-import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.flow.RedirectToUrlException;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
@@ -131,7 +127,7 @@ public static void actionLogout(Component aOwner,
}
if (isNotBlank(aSecProperties.getAutoLogin())) {
- PageParameters parameters = new PageParameters();
+ var parameters = new PageParameters();
parameters.set(LoginPage.PARAM_SKIP_AUTO_LOGIN, true);
aOwner.setResponsePage(LoginPage.class, parameters);
return;
@@ -139,21 +135,4 @@ public static void actionLogout(Component aOwner,
aOwner.setResponsePage(aOwner.getApplication().getHomePage());
}
-
- /**
- * Checks if auto-logout is enabled. For Winstone, we get a max session length of 0, so here it
- * is disabled.
- */
- private int getAutoLogoutTime()
- {
- int duration = 0;
- Request request = RequestCycle.get().getRequest();
- if (request instanceof ServletWebRequest) {
- HttpSession session = ((ServletWebRequest) request).getContainerRequest().getSession();
- if (session != null) {
- duration = session.getMaxInactiveInterval();
- }
- }
- return duration;
- }
}
diff --git a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtension.js b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtension.js
similarity index 100%
rename from inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtension.js
rename to inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtension.js
diff --git a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtensionBehavior.java b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtensionBehavior.java
similarity index 98%
rename from inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtensionBehavior.java
rename to inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtensionBehavior.java
index a2a8d5a6223..a548e240843 100644
--- a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtensionBehavior.java
+++ b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtensionBehavior.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package de.tudarmstadt.ukp.inception.websocket.feedback;
+package de.tudarmstadt.ukp.inception.ui.core.feedback;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
diff --git a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtensionJavascriptReference.java b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtensionJavascriptReference.java
similarity index 96%
rename from inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtensionJavascriptReference.java
rename to inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtensionJavascriptReference.java
index e55fa3da271..d73bc2c4ef3 100644
--- a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/feedback/FeedbackPanelExtensionJavascriptReference.java
+++ b/inception/inception-ui-core/src/main/java/de/tudarmstadt/ukp/inception/ui/core/feedback/FeedbackPanelExtensionJavascriptReference.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package de.tudarmstadt.ukp.inception.websocket.feedback;
+package de.tudarmstadt.ukp.inception.ui.core.feedback;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
diff --git a/inception/inception-ui-scheduling/pom.xml b/inception/inception-ui-scheduling/pom.xml
index 02cc7dfab32..cb2045aa93d 100644
--- a/inception/inception-ui-scheduling/pom.xml
+++ b/inception/inception-ui-scheduling/pom.xml
@@ -45,6 +45,14 @@
de.tudarmstadt.ukp.inception.app
inception-security
+
+ de.tudarmstadt.ukp.inception.app
+ inception-model
+
+
+ de.tudarmstadt.ukp.inception.app
+ inception-project
+
@@ -75,20 +83,12 @@
org.springframework.security
spring-security-core
-
- org.springframework.security
- spring-security-web
-
org.apache.wicket
wicket-core
-
- org.apache.wicket
- wicket-request
-
org.apache.wicket
wicket-spring
diff --git a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorFooterItem.java b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorFooterItem.java
index ba9bd60bd5b..a76636732ee 100644
--- a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorFooterItem.java
+++ b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorFooterItem.java
@@ -29,6 +29,8 @@ public class TaskMonitorFooterItem
@Override
public Component create(String aId)
{
- return new TaskMonitorPanel(aId).setShowFinishedTasks(true).setPopupMode(true);
+ return new TaskMonitorPanel(aId) //
+ .setShowFinishedTasks(true) //
+ .setPopupMode(true);
}
}
diff --git a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorPanel.java b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorPanel.java
index 6c31be1da97..c02cb51a64b 100644
--- a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorPanel.java
+++ b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/TaskMonitorPanel.java
@@ -17,25 +17,23 @@
*/
package de.tudarmstadt.ukp.inception.ui.scheduling;
+import static de.tudarmstadt.ukp.clarin.webanno.security.WicketSecurityUtils.getCsrfTokenFromSession;
+import static de.tudarmstadt.ukp.inception.support.wicket.WicketUtil.constructEndpointUrl;
+import static de.tudarmstadt.ukp.inception.support.wicket.WicketUtil.constructWsEndpointUrl;
import static de.tudarmstadt.ukp.inception.websocket.config.WebsocketConfig.WS_ENDPOINT;
-import static java.lang.String.format;
+import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.Map;
import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.authorization.Action;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeAction;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.model.Model;
-import org.apache.wicket.request.Url;
-import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.spring.injection.annot.SpringBean;
-import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
+import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerController;
import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerWebsocketController;
import de.tudarmstadt.ukp.inception.support.svelte.SvelteBehavior;
@@ -47,15 +45,44 @@ public class TaskMonitorPanel
private static final long serialVersionUID = -9006607500867612027L;
private @SpringBean ServletContext servletContext;
+ private @SpringBean SchedulerWebsocketController schedulerWebsocketController;
+ private final String taskStatusTopic;
+ private final String taskUpdatesTopic;
private boolean popupMode = true;
private boolean showFinishedTasks = true;
private String typePattern = "";
+ /**
+ * Create a monitoring panel that subscribes to all events for the current user.
+ *
+ * @param aId
+ * The non-null id of this component
+ */
public TaskMonitorPanel(String aId)
{
super(aId);
setOutputMarkupPlaceholderTag(true);
+ taskStatusTopic = "/app" + SchedulerWebsocketController.getUserTaskUpdatesTopic();
+ taskUpdatesTopic = "/user/queue" + SchedulerWebsocketController.getUserTaskUpdatesTopic();
+ }
+
+ /**
+ * Create a monitoring panel that subscribes to all events for the given project.
+ *
+ * @param aId
+ * The non-null id of this component
+ * @param aProject
+ * The project to monitor.
+ */
+ public TaskMonitorPanel(String aId, Project aProject)
+ {
+ super(aId);
+ setOutputMarkupPlaceholderTag(true);
+ taskStatusTopic = "/app"
+ + SchedulerWebsocketController.getProjectTaskUpdatesTopic(aProject);
+ taskUpdatesTopic = "/topic"
+ + SchedulerWebsocketController.getProjectTaskUpdatesTopic(aProject);
}
public TaskMonitorPanel setPopupMode(boolean aPopupMode)
@@ -72,7 +99,7 @@ public TaskMonitorPanel setShowFinishedTasks(boolean aKeepRemovedTasks)
public TaskMonitorPanel setTypePattern(String aTypePattern)
{
- if (StringUtils.isBlank(aTypePattern)) {
+ if (isBlank(aTypePattern)) {
typePattern = "";
}
else {
@@ -92,42 +119,11 @@ protected void onConfigure()
"popupMode", popupMode, //
"showFinishedTasks", showFinishedTasks, //
"typePattern", typePattern, //
- "endpointUrl", constructEndpointUrl(), //
- "wsEndpointUrl", constructWsEndpointUrl(), //
- "topicChannel", SchedulerWebsocketController.BASE_TOPIC)));
+ "endpointUrl", constructEndpointUrl(SchedulerController.BASE_URL), //
+ "wsEndpointUrl", constructWsEndpointUrl(WS_ENDPOINT), //
+ "taskStatusTopic", taskStatusTopic, //
+ "taskUpdatesTopic", taskUpdatesTopic)));
add(new SvelteBehavior());
}
-
- private String constructEndpointUrl()
- {
- Url endPointUrl = Url.parse(
- format("%s%s", servletContext.getContextPath(), SchedulerController.BASE_URL));
- return RequestCycle.get().getUrlRenderer().renderFullUrl(endPointUrl);
- }
-
- private String constructWsEndpointUrl()
- {
- Url endPointUrl = Url.parse(format("%s%s", servletContext.getContextPath(), WS_ENDPOINT));
- endPointUrl.setProtocol("ws");
- return RequestCycle.get().getUrlRenderer().renderFullUrl(endPointUrl);
- }
-
- public String getCsrfTokenFromSession()
- {
- var httpRequest = (HttpServletRequest) RequestCycle.get().getRequest()
- .getContainerRequest();
- var httpResponse = (HttpServletResponse) RequestCycle.get().getResponse()
- .getContainerResponse();
-
- var csrfTokenRepository = new HttpSessionCsrfTokenRepository();
- var csrfToken = csrfTokenRepository.loadDeferredToken(httpRequest, httpResponse);
-
- if (csrfToken != null) {
- return csrfToken.get().getToken();
- }
- else {
- return "";
- }
- }
}
diff --git a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/config/SchedulingUiAutoConfiguration.java b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/config/SchedulingUiAutoConfiguration.java
index a7ac8824d3a..568436feebe 100644
--- a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/config/SchedulingUiAutoConfiguration.java
+++ b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/config/SchedulingUiAutoConfiguration.java
@@ -22,6 +22,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import de.tudarmstadt.ukp.clarin.webanno.project.ProjectAccess;
import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
import de.tudarmstadt.ukp.inception.scheduling.SchedulingService;
import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerController;
@@ -40,8 +41,9 @@ public TaskMonitorFooterItem taskMonitorFooterItem()
}
@Bean
- SchedulerController schedulerController(SchedulingService aSchedulingService, UserDao aUserDao)
+ SchedulerController schedulerController(SchedulingService aSchedulingService, UserDao aUserDao,
+ ProjectAccess aProjectAccess)
{
- return new SchedulerControllerImpl(aSchedulingService, aUserDao);
+ return new SchedulerControllerImpl(aSchedulingService, aUserDao, aProjectAccess);
}
}
diff --git a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerControllerImpl.java b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerControllerImpl.java
index 2b79c8612e0..0d61b6a0f02 100644
--- a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerControllerImpl.java
+++ b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerControllerImpl.java
@@ -26,8 +26,12 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import de.tudarmstadt.ukp.clarin.webanno.project.ProjectAccess;
import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
+import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
+import de.tudarmstadt.ukp.inception.scheduling.ProjectTask;
import de.tudarmstadt.ukp.inception.scheduling.SchedulingService;
+import de.tudarmstadt.ukp.inception.scheduling.Task;
import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerController;
@ConditionalOnWebApplication
@@ -38,11 +42,14 @@ public class SchedulerControllerImpl
{
private final SchedulingService schedulingService;
private final UserDao userService;
+ private final ProjectAccess projectAccess;
- public SchedulerControllerImpl(SchedulingService aSchedulingService, UserDao aUserDao)
+ public SchedulerControllerImpl(SchedulingService aSchedulingService, UserDao aUserDao,
+ ProjectAccess aProjectAccess)
{
schedulingService = aSchedulingService;
userService = aUserDao;
+ projectAccess = aProjectAccess;
}
@PostMapping(//
@@ -51,10 +58,10 @@ public SchedulerControllerImpl(SchedulingService aSchedulingService, UserDao aUs
produces = APPLICATION_JSON_VALUE)
public void cancelTask(@PathVariable(PARAM_TASK_ID) int aTaskId)
{
- var user = userService.getCurrentUser();
+ var sessionOwner = userService.getCurrentUser();
schedulingService.stopAllTasksMatching(
- t -> t.getId() == aTaskId && t.getUser().filter(user::equals).isPresent());
+ t -> t.getId() == aTaskId && canPerformActionOnTask(t, sessionOwner));
}
@PostMapping(//
@@ -63,9 +70,18 @@ public void cancelTask(@PathVariable(PARAM_TASK_ID) int aTaskId)
produces = APPLICATION_JSON_VALUE)
public void acknowledgeResult(@PathVariable(PARAM_TASK_ID) int aTaskId)
{
- var user = userService.getCurrentUser();
+ var sessionOwner = userService.getCurrentUser();
schedulingService.stopAllTasksMatching(
- t -> t.getId() == aTaskId && t.getUser().filter(user::equals).isPresent());
+ t -> t.getId() == aTaskId && canPerformActionOnTask(t, sessionOwner));
+ }
+
+ private boolean canPerformActionOnTask(Task aTask, User aUser)
+ {
+ if (aTask instanceof ProjectTask) {
+ return projectAccess.canManageProject(String.valueOf(aTask.getProject().getId()));
+ }
+
+ return aTask.getUser().filter(aUser::equals).isPresent();
}
}
diff --git a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerWebsocketControllerImpl.java b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerWebsocketControllerImpl.java
index 4ad6654b1c2..5b8589a7d78 100644
--- a/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerWebsocketControllerImpl.java
+++ b/inception/inception-ui-scheduling/src/main/java/de/tudarmstadt/ukp/inception/ui/scheduling/controller/SchedulerWebsocketControllerImpl.java
@@ -17,19 +17,29 @@
*/
package de.tudarmstadt.ukp.inception.ui.scheduling.controller;
+import static de.tudarmstadt.ukp.inception.websocket.config.WebSocketConstants.PARAM_PROJECT;
+
+import java.io.IOException;
import java.security.Principal;
import java.util.List;
import java.util.Objects;
+import javax.servlet.ServletContext;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
+import org.springframework.messaging.handler.annotation.SendTo;
+import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
+import de.tudarmstadt.ukp.inception.scheduling.ProjectTask;
import de.tudarmstadt.ukp.inception.scheduling.SchedulingService;
import de.tudarmstadt.ukp.inception.scheduling.controller.SchedulerWebsocketController;
import de.tudarmstadt.ukp.inception.scheduling.controller.model.MTaskStateUpdate;
@@ -41,15 +51,19 @@ public class SchedulerWebsocketControllerImpl
implements SchedulerWebsocketController
{
private final SchedulingService schedulingService;
+ private final SimpMessagingTemplate msgTemplate;
@Autowired
- public SchedulerWebsocketControllerImpl(SchedulingService aSchedulingService)
+ public SchedulerWebsocketControllerImpl(SchedulingService aSchedulingService,
+ ServletContext aServletContext, SimpMessagingTemplate aMsgTemplate)
{
+ msgTemplate = aMsgTemplate;
schedulingService = aSchedulingService;
}
- @SubscribeMapping(SchedulerWebsocketController.BASE_TOPIC + "/tasks")
- public List getCurrentTaskStates(Principal user) throws AccessDeniedException
+ @SubscribeMapping(SchedulerWebsocketController.USER_TASKS_TOPIC)
+ public List onSubscribeToUserTaskUpdates(Principal user)
+ throws AccessDeniedException
{
return schedulingService.getAllTasks().stream() //
.filter(t -> t.getParentTask() == null) //
@@ -61,6 +75,44 @@ public List getCurrentTaskStates(Principal user) throws Access
.toList();
}
+ @SubscribeMapping(PROJECT_TASKS_TOPIC_TEMPLATE)
+ public List onSubscribeToProjectTaskUpdates(
+ SimpMessageHeaderAccessor aHeaderAccessor, Principal aPrincipal, //
+ @DestinationVariable(PARAM_PROJECT) long aProjectId)
+ throws IOException
+ {
+ return schedulingService.getAllTasks().stream() //
+ .filter(t -> t.getParentTask() == null) //
+ .filter(ProjectTask.class::isInstance) //
+ .map(t -> t.getMonitor()) //
+ .filter(Objects::nonNull) //
+ .filter(t -> t.getProject() != null) //
+ .filter(t -> Objects.equals(t.getProject().getId(), aProjectId)) //
+ .map(MTaskStateUpdate::new) //
+ .toList();
+ }
+
+ @Override
+ public void dispatch(MTaskStateUpdate aUpdate)
+ {
+ if (aUpdate.getUsername() != null) {
+ msgTemplate.convertAndSendToUser(aUpdate.getUsername(),
+ "/queue" + SchedulerWebsocketController.USER_TASKS_TOPIC, aUpdate);
+ }
+
+ if (aUpdate.getProjectId() > 0) {
+ var topic = SchedulerWebsocketController
+ .getProjectTaskUpdatesTopic(aUpdate.getProjectId());
+ msgTemplate.convertAndSend("/topic" + topic, aUpdate);
+ }
+ }
+
+ @SendTo(PROJECT_TASKS_TOPIC_TEMPLATE)
+ public MTaskStateUpdate send(MTaskStateUpdate aUpdate)
+ {
+ return aUpdate;
+ }
+
@MessageExceptionHandler
@SendToUser("/queue/errors")
public String handleException(Throwable exception)
diff --git a/inception/inception-ui-scheduling/src/main/ts/src/TaskMonitorPanel.svelte b/inception/inception-ui-scheduling/src/main/ts/src/TaskMonitorPanel.svelte
index 480df9010f8..8671d88ddab 100644
--- a/inception/inception-ui-scheduling/src/main/ts/src/TaskMonitorPanel.svelte
+++ b/inception/inception-ui-scheduling/src/main/ts/src/TaskMonitorPanel.svelte
@@ -24,7 +24,8 @@
export let csrfToken: string
export let endpointUrl: string // should this be full http://... url
export let wsEndpointUrl: string // should this be full ws://... url
- export let topicChannel: string
+ export let taskStatusTopic: string
+ export let taskUpdatesTopic: string
export let tasks: MTaskStateUpdate[] = []
export let connected = false
export let popupMode = true
@@ -56,7 +57,7 @@
);
});
stompClient.subscribe(
- "/app" + topicChannel + "/tasks",
+ taskStatusTopic,
function (msg) {
tasks = JSON.parse(msg.body) || []
if (!showFinishedTasks) {
@@ -65,7 +66,7 @@
}
);
stompClient.subscribe(
- "/user/queue" + topicChannel + "/tasks",
+ taskUpdatesTopic,
function (msg) {
var msgBody = JSON.parse(msg.body) as MTaskStateUpdate;
diff --git a/inception/inception-websocket/pom.xml b/inception/inception-websocket/pom.xml
index 511c47b3045..903b118ce60 100644
--- a/inception/inception-websocket/pom.xml
+++ b/inception/inception-websocket/pom.xml
@@ -25,43 +25,15 @@
inception-websocket
INCEpTION - Websocket
-
- de.tudarmstadt.ukp.inception.app
- inception-documents-api
-
-
- de.tudarmstadt.ukp.inception.app
- inception-project-api
-
de.tudarmstadt.ukp.inception.app
inception-security
-
- de.tudarmstadt.ukp.inception.app
- inception-support-bootstrap
-
de.tudarmstadt.ukp.inception.app
inception-ui-core
-
- de.tudarmstadt.ukp.inception.app
- inception-model
-
-
- de.tudarmstadt.ukp.inception.app
- inception-log
-
-
- org.apache.wicket
- wicket-core
-
-
- org.apache.wicket
- wicket-util
-
com.giffing.wicket.spring.boot.starter
wicket-spring-boot-starter
@@ -75,7 +47,7 @@
org.springframework
- spring-core
+ spring-messaging
org.springframework
@@ -85,10 +57,6 @@
org.springframework
spring-context
-
- org.springframework
- spring-messaging
-
org.springframework
spring-websocket
@@ -118,125 +86,5 @@
org.slf4j
slf4j-api
-
-
- javax.persistence
- javax.persistence-api
-
-
-
- org.apache.uima
- uimaj-core
- test
-
-
- org.apache.tomcat.embed
- tomcat-embed-websocket
- test
-
-
- org.springframework.security
- spring-security-crypto
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
- test
-
-
- org.springframework.boot
- spring-boot-test
- test
-
-
- org.springframework.boot
- spring-boot-starter-web
- test
-
-
- org.springframework.boot
- spring-boot-test-autoconfigure
- test
-
-
- org.springframework
- spring-test
- test
-
-
- org.springframework.security
- spring-security-test
- test
-
-
- org.springframework.security
- spring-security-web
- test
-
-
- org.hsqldb
- hsqldb
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-support
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-project
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-documents
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-api-annotation
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-schema
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-annotation-storage
- test
-
-
- de.tudarmstadt.ukp.inception.app
- inception-export
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
-
- org.springframework:spring-core
- org.springframework.boot:spring-boot-starter-web
- org.springframework.boot:spring-boot-test-autoconfigure
-
-
-
-
-
-
diff --git a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketAutoConfiguration.java b/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketAutoConfiguration.java
index b2e91f0f2c9..d0c394b3b58 100644
--- a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketAutoConfiguration.java
+++ b/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketAutoConfiguration.java
@@ -17,7 +17,6 @@
*/
package de.tudarmstadt.ukp.inception.websocket.config;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -27,12 +26,9 @@
import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.csrf.CsrfAttacksPreventionProperties;
-import de.tudarmstadt.ukp.inception.log.config.EventLoggingAutoConfiguration;
-
@ConditionalOnWebApplication
@Configuration
@EnableWebSocketMessageBroker
-@AutoConfigureAfter(EventLoggingAutoConfiguration.class)
@ConditionalOnProperty(prefix = "websocket", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(CsrfAttacksPreventionProperties.class)
public class WebsocketAutoConfiguration
diff --git a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketConfig.java b/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketConfig.java
index 5df487a4c73..742bbad8619 100644
--- a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketConfig.java
+++ b/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketConfig.java
@@ -64,9 +64,10 @@ public void configureMessageBroker(MessageBrokerRegistry aRegistry)
// broker will send to destinations with this prefix, queue is custom for user-specific
// channels. client will subscribe to /queue/{subtopic} where subtopic is a specific topic
// that controller or service will address messages to
- aRegistry.enableSimpleBroker("/queue", "/topic");
+ aRegistry.enableSimpleBroker("/queue/", "/topic/");
// clients should send messages to channels pre-fixed with this
- aRegistry.setApplicationDestinationPrefixes("/app");
+ aRegistry.setApplicationDestinationPrefixes("/app/");
+ aRegistry.setUserDestinationPrefix("/user/");
// messages to clients are by default not ordered, need to explicitly set order here
aRegistry.setPreservePublishOrder(true);
}
diff --git a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketSecurityConfig.java b/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketSecurityConfig.java
index 740871bfd24..8edea57ea09 100644
--- a/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketSecurityConfig.java
+++ b/inception/inception-websocket/src/main/java/de/tudarmstadt/ukp/inception/websocket/config/WebsocketSecurityConfig.java
@@ -74,8 +74,11 @@ protected void configureInbound(MessageSecurityMetadataSourceRegistry aSecurityR
.simpTypeMatchers(DISCONNECT).permitAll()
// messages other than MESSAGE,SUBSCRIBE are allowed for authenticated users
.nullDestMatcher().authenticated() //
+ .simpSubscribeDestMatchers("/*/errors*").hasRole("USER")
.simpSubscribeDestMatchers("/*/loggedEvents").hasRole("ADMIN")
- .simpSubscribeDestMatchers("/*/scheduler").hasRole("USER")
+ .simpSubscribeDestMatchers("/*/scheduler/user").hasRole("USER")
+ .simpSubscribeDestMatchers("/*/scheduler" + TOPIC_ELEMENT_PROJECT + "{" + PARAM_PROJECT + "}")
+ .access("@projectAccess.canManageProject(#" + PARAM_PROJECT + ")")
.simpSubscribeDestMatchers("/*" + NS_PROJECT + "/{" + PARAM_PROJECT + "}/exports")
.access("@projectAccess.canManageProject(#" + PARAM_PROJECT + ")")
.simpSubscribeDestMatchers(annotationEditorTopic)
diff --git a/inception/installEclipseSettings.sh b/inception/installEclipseSettings.sh
index 181640b7b45..33324870e95 100755
--- a/inception/installEclipseSettings.sh
+++ b/inception/installEclipseSettings.sh
@@ -84,6 +84,7 @@ installPrefs inception-kb
installPrefs inception-kb-fact-linking
installPrefs inception-layer-docmetadata
installPrefs inception-log
+installPrefs inception-log-ui
installPrefs inception-model
installPrefs inception-model-export
installPrefs inception-pdf-editor
diff --git a/inception/pom.xml b/inception/pom.xml
index 022d376bd8d..d0fe6fb912c 100644
--- a/inception/pom.xml
+++ b/inception/pom.xml
@@ -170,6 +170,7 @@
inception-telemetry
inception-ui-core
inception-log
+
inception-log-ui
inception-scheduling
inception-js-api
inception-annotation-storage