You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2018/01/04 08:13:21 UTC

[06/21] james-project git commit: JAMES-2272 Webadmin should expose a task management API

JAMES-2272 Webadmin should expose a task management API


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/d9d73f80
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/d9d73f80
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/d9d73f80

Branch: refs/heads/master
Commit: d9d73f801fa61dc311fce2a3566994bc32068a22
Parents: 1b7e8d7
Author: benwa <bt...@linagora.com>
Authored: Wed Dec 27 12:16:17 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Thu Jan 4 15:03:36 2018 +0700

----------------------------------------------------------------------
 .../james/modules/server/TaskRoutesModule.java  |  37 +++
 .../modules/server/WebAdminServerModule.java    |   2 +
 server/protocols/webadmin/webadmin-core/pom.xml |   9 +
 .../james/webadmin/dto/ExecutionDetailsDto.java |  95 +++++++
 .../apache/james/webadmin/dto/TaskIdDto.java    |  40 +++
 .../james/webadmin/routes/TasksRoutes.java      | 127 +++++++++
 .../james/webadmin/utils/ErrorResponder.java    |   1 +
 .../james/webadmin/utils/JsonExtractor.java     |   6 +-
 .../james/webadmin/routes/TasksRoutesTest.java  | 277 +++++++++++++++++++
 9 files changed, 592 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java
new file mode 100644
index 0000000..390bb17
--- /dev/null
+++ b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/TaskRoutesModule.java
@@ -0,0 +1,37 @@
+/****************************************************************
+ * 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.james.modules.server;
+
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.routes.TasksRoutes;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+import com.google.inject.multibindings.Multibinder;
+
+public class TaskRoutesModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind(TasksRoutes.class).in(Scopes.SINGLETON);
+
+        Multibinder<Routes> routesMultibinder = Multibinder.newSetBinder(binder(), Routes.class);
+        routesMultibinder.addBinding().to(TasksRoutes.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
index 1a140ee..6142be1 100644
--- a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
+++ b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
@@ -69,6 +69,8 @@ public class WebAdminServerModule extends AbstractModule {
 
     @Override
     protected void configure() {
+        install(new TaskRoutesModule());
+
         bind(JsonTransformer.class).in(Scopes.SINGLETON);
         bind(WebAdminServer.class).in(Scopes.SINGLETON);
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/pom.xml b/server/protocols/webadmin/webadmin-core/pom.xml
index 56bcda1..e86562d 100644
--- a/server/protocols/webadmin/webadmin-core/pom.xml
+++ b/server/protocols/webadmin/webadmin-core/pom.xml
@@ -55,6 +55,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>james-server-task</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
         </dependency>
@@ -75,6 +79,11 @@
             <artifactId>guava</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.jayway.restassured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>com.sparkjava</groupId>
             <artifactId>spark-core</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java
new file mode 100644
index 0000000..7069e8b
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/ExecutionDetailsDto.java
@@ -0,0 +1,95 @@
+/****************************************************************
+ * 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.james.webadmin.dto;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.apache.james.task.TaskExecutionDetails;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.github.steveash.guavate.Guavate;
+
+public class ExecutionDetailsDto {
+    public static List<ExecutionDetailsDto> from(List<TaskExecutionDetails> tasksDetails) {
+        return tasksDetails.stream()
+            .map(ExecutionDetailsDto::new)
+            .collect(Guavate.toImmutableList());
+    }
+
+    public static ExecutionDetailsDto from(TaskExecutionDetails taskDetails) {
+        return new ExecutionDetailsDto(taskDetails);
+    }
+
+    private final TaskExecutionDetails executionDetails;
+
+    private ExecutionDetailsDto(TaskExecutionDetails executionDetails) {
+        this.executionDetails = executionDetails;
+    }
+
+    public UUID getTaskId() {
+        return executionDetails.getTaskId().getValue();
+    }
+
+    public String getType() {
+        return executionDetails.getType();
+    }
+
+    public String getStatus() {
+        return executionDetails.getStatus().getValue();
+    }
+
+    public Optional<TaskExecutionDetails.AdditionalInformation> getAdditionalInformation() {
+        return executionDetails.getAdditionalInformation();
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_ABSENT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+    public Optional<ZonedDateTime> getSubmitDate() {
+        return executionDetails.getSubmitDate();
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_ABSENT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+    public Optional<ZonedDateTime> getStartedDate() {
+        return executionDetails.getStartedDate();
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_ABSENT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+    public Optional<ZonedDateTime> getCompletedDate() {
+        return executionDetails.getCompletedDate();
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_ABSENT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+    public Optional<ZonedDateTime> getCanceledDate() {
+        return executionDetails.getCanceledDate();
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_ABSENT)
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+    public Optional<ZonedDateTime> getFailedDate() {
+        return executionDetails.getFailedDate();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java
new file mode 100644
index 0000000..231d051
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/TaskIdDto.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * 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.james.webadmin.dto;
+
+import java.util.UUID;
+
+import org.apache.james.task.TaskId;
+
+public class TaskIdDto {
+    public static TaskIdDto from(TaskId id) {
+        return new TaskIdDto(id.getValue());
+    }
+
+    private final UUID uuid;
+
+    public TaskIdDto(UUID uuid) {
+        this.uuid = uuid;
+    }
+
+    public UUID getTaskId() {
+        return uuid;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java
new file mode 100644
index 0000000..99bd2df
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/TasksRoutes.java
@@ -0,0 +1,127 @@
+/****************************************************************
+ * 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.james.webadmin.routes;
+
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+import javax.inject.Inject;
+
+import org.apache.james.task.TaskExecutionDetails;
+import org.apache.james.task.TaskId;
+import org.apache.james.task.TaskManager;
+import org.apache.james.task.TaskNotFoundException;
+import org.apache.james.webadmin.Constants;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.dto.ExecutionDetailsDto;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+
+import spark.Request;
+import spark.Response;
+import spark.Service;
+
+public class TasksRoutes implements Routes {
+    public static final String BASE = "/tasks";
+    private final TaskManager taskManager;
+    private final JsonTransformer jsonTransformer;
+
+    @Inject
+    public TasksRoutes(TaskManager taskManager, JsonTransformer jsonTransformer) {
+        this.taskManager = taskManager;
+        this.jsonTransformer = jsonTransformer;
+    }
+
+    @Override
+    public void define(Service service) {
+        service.get(BASE + "/:id", this::getStatus, jsonTransformer);
+
+        service.get(BASE + "/:id/await", this::await, jsonTransformer);
+
+        service.delete(BASE + "/:id", this::cancel, jsonTransformer);
+
+        service.get(BASE, this::list, jsonTransformer);
+    }
+
+    private Object list(Request req, Response response) {
+        try {
+            return ExecutionDetailsDto.from(
+                Optional.ofNullable(req.queryParams("status"))
+                .map(TaskManager.Status::fromString)
+                .map(taskManager::list)
+                .orElse(taskManager.list()));
+        } catch (IllegalArgumentException e) {
+            throw ErrorResponder.builder()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .cause(e)
+                .message("Invalid status query parameter")
+                .haltError();
+        }
+    }
+
+    private Object getStatus(Request req, Response response) {
+        TaskId taskId = getTaskId(req);
+        return respondStatus(taskId,
+            () -> taskManager.getExecutionDetails(getTaskId(req)));
+    }
+
+    private Object await(Request req, Response response) {
+        TaskId taskId = getTaskId(req);
+        return respondStatus(taskId,
+            () -> taskManager.await(getTaskId(req)));
+    }
+
+    private Object respondStatus(TaskId taskId, Supplier<TaskExecutionDetails> executionDetailsSupplier) {
+        try {
+            TaskExecutionDetails executionDetails = executionDetailsSupplier.get();
+            return ExecutionDetailsDto.from(executionDetails);
+        } catch (TaskNotFoundException e) {
+            throw ErrorResponder.builder()
+                .message(String.format("%s can not be found", taskId.getValue()))
+                .statusCode(HttpStatus.NOT_FOUND_404)
+                .type(ErrorResponder.ErrorType.NOT_FOUND)
+                .haltError();
+        }
+    }
+
+    private Object cancel(Request req, Response response) {
+        TaskId taskId = getTaskId(req);
+        taskManager.cancel(taskId);
+        response.status(HttpStatus.NO_CONTENT_204);
+        return Constants.EMPTY_BODY;
+    }
+
+    private TaskId getTaskId(Request req) {
+        try {
+            String id = req.params("id");
+            return new TaskId(UUID.fromString(id));
+        } catch (Exception e) {
+            throw ErrorResponder.builder()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .cause(e)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .message("Invalid task id")
+                .haltError();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
index c87f0f9..b02883d 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
@@ -33,6 +33,7 @@ import spark.HaltException;
 public class ErrorResponder {
     public enum ErrorType {
         INVALID_ARGUMENT("InvalidArgument"),
+        NOT_FOUND("notFound"),
         WRONG_STATE("WrongState"),
         SERVER_ERROR("ServerError");
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
index f4eb420..02ebe50 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
@@ -22,6 +22,7 @@ package org.apache.james.webadmin.utils;
 import java.io.IOException;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 
 public class JsonExtractor<Request> {
 
@@ -29,14 +30,15 @@ public class JsonExtractor<Request> {
     private final Class<Request> type;
 
     public JsonExtractor(Class<Request> type) {
-        this.objectMapper = new ObjectMapper();
+        this.objectMapper = new ObjectMapper()
+            .registerModule(new Jdk8Module());
         this.type = type;
     }
 
     public Request parse(String text) throws JsonExtractException {
         try {
             return objectMapper.readValue(text, type);
-        } catch (IOException e) {
+        } catch (IOException | IllegalArgumentException e) {
             throw new JsonExtractException(e);
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d9d73f80/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java
new file mode 100644
index 0000000..5403349
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/routes/TasksRoutesTest.java
@@ -0,0 +1,277 @@
+/****************************************************************
+ * 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.james.webadmin.routes;
+
+import static com.jayway.restassured.RestAssured.given;
+import static com.jayway.restassured.RestAssured.when;
+import static com.jayway.restassured.RestAssured.with;
+import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
+import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
+import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.james.metrics.logger.DefaultMetricFactory;
+import org.apache.james.task.MemoryTaskManager;
+import org.apache.james.task.Task;
+import org.apache.james.task.TaskId;
+import org.apache.james.task.TaskManager;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.jayway.restassured.RestAssured;
+import com.jayway.restassured.builder.RequestSpecBuilder;
+import com.jayway.restassured.http.ContentType;
+
+public class TasksRoutesTest {
+
+    private MemoryTaskManager taskManager;
+    private WebAdminServer webAdminServer;
+
+    @Before
+    public void setUp() throws Exception {
+        taskManager = new MemoryTaskManager();
+
+        webAdminServer = WebAdminUtils.createWebAdminServer(
+            new DefaultMetricFactory(),
+            new TasksRoutes(taskManager, new JsonTransformer()));
+
+        webAdminServer.configure(NO_CONFIGURATION);
+        webAdminServer.await();
+
+        RestAssured.requestSpecification = new RequestSpecBuilder()
+            .setContentType(ContentType.JSON)
+            .setAccept(ContentType.JSON)
+            .setBasePath(TasksRoutes.BASE)
+            .setPort(webAdminServer.getPort().get().getValue())
+            .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)))
+            .build();
+    }
+
+    @After
+    public void tearDown() {
+        taskManager.stop();
+        webAdminServer.destroy();
+    }
+
+    @Test
+    public void listShouldReturnEmptyWhenNoTask() {
+        when()
+            .get()
+        .then()
+            .body("", hasSize(0));
+    }
+
+    @Test
+    public void listShouldReturnTaskDetailsWhenTaskInProgress() {
+        TaskId taskId = taskManager.submit(() -> {
+            await();
+            return Task.Result.COMPLETED;
+        });
+
+        when()
+            .get()
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(1))
+            .body("[0].status", is(TaskManager.Status.IN_PROGRESS.getValue()))
+            .body("[0].taskId", is(taskId.getValue().toString()))
+            .body("[0].class", is(not(empty())));
+    }
+
+    private void await() {
+        try {
+            new CountDownLatch(1).await();
+        } catch (InterruptedException e) {
+            Throwables.propagate(e);
+        }
+    }
+
+    @Test
+    public void listShouldListTaskWhenStatusFilter() {
+        TaskId taskId = taskManager.submit(() -> {
+            await();
+            return Task.Result.COMPLETED;
+        });
+
+        given()
+            .param("status", TaskManager.Status.IN_PROGRESS.getValue())
+        .when()
+            .get()
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(1))
+            .body("[0].status", is(TaskManager.Status.IN_PROGRESS.getValue()))
+            .body("[0].taskId", is(taskId.getValue().toString()))
+            .body("[0].type", is(Task.UNKNOWN));
+    }
+
+    @Test
+    public void listShouldReturnEmptyWhenNonMatchingStatusFilter() {
+        taskManager.submit(() -> {
+            await();
+            return Task.Result.COMPLETED;
+        });
+
+        given()
+            .param("status", TaskManager.Status.WAITING.getValue())
+        .when()
+            .get()
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(0));
+    }
+
+    @Test
+    public void getShouldReturnTaskDetails() {
+        TaskId taskId = taskManager.submit(() -> {
+            await();
+            return Task.Result.COMPLETED;
+        });
+
+        when()
+            .get("/" + taskId.getValue())
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("status", is("inProgress"));
+    }
+
+    @Test
+    public void getAwaitShouldAwaitTaskCompletion() {
+        TaskId taskId = taskManager.submit(() -> {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                throw Throwables.propagate(e);
+            }
+            return Task.Result.COMPLETED;
+        });
+
+        when()
+            .get("/" + taskId.getValue() + "/await")
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("status", is("completed"));
+    }
+
+    @Test
+    public void deleteShouldReturnOk() {
+        TaskId taskId = taskManager.submit(() -> {
+            await();
+            return Task.Result.COMPLETED;
+        });
+
+        when()
+            .delete("/" + taskId.getValue())
+        .then()
+            .statusCode(HttpStatus.NO_CONTENT_204);
+    }
+
+    @Test
+    public void deleteShouldCancelMatchingTask() {
+        TaskId taskId = taskManager.submit(() -> {
+            await();
+            return Task.Result.COMPLETED;
+        });
+
+        with()
+            .delete("/" + taskId.getValue());
+
+        when()
+            .get("/" + taskId.getValue())
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("status", is("canceled"));
+    }
+
+    @Test
+    public void getShouldReturnNotFoundWhenIdDoesNotExist() {
+        String taskId = UUID.randomUUID().toString();
+
+        when()
+            .get("/" + taskId)
+        .then()
+            .statusCode(HttpStatus.NOT_FOUND_404)
+            .body("statusCode", is(HttpStatus.NOT_FOUND_404))
+            .body("type", is("notFound"))
+            .body("message", is(String.format("%s can not be found", taskId)));
+    }
+
+    @Test
+    public void getShouldReturnErrorWhenInvalidId() {
+        String taskId = "invalid";
+
+        when()
+            .get("/" + taskId)
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
+            .body("type", is("InvalidArgument"))
+            .body("message", is("Invalid task id"));
+    }
+
+    @Test
+    public void deleteShouldReturnOkWhenNonExistingId() {
+        String taskId = UUID.randomUUID().toString();
+
+        when()
+            .delete("/" + taskId)
+        .then()
+            .statusCode(HttpStatus.NO_CONTENT_204);
+    }
+
+    @Test
+    public void deleteShouldReturnAnErrorOnInvalidId() {
+        String taskId = "invalid";
+
+        when()
+            .delete("/" + taskId)
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
+            .body("type", is("InvalidArgument"))
+            .body("message", is("Invalid task id"));
+    }
+
+    @Test
+    public void listShouldReturnErrorWhenNonExistingStatus() {
+        given()
+            .param("status", "invalid")
+            .get()
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
+            .body("type", is("InvalidArgument"))
+            .body("message", is("Invalid status query parameter"));
+    }
+
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org