This is an automated email from the ASF dual-hosted git repository. rouazana pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 21f5793a9c10f9af67adf22f12f0c2c0bc4c7f24 Author: Matthieu Baechler <[email protected]> AuthorDate: Mon Jul 15 17:35:32 2019 +0200 JAMES-2813 Introduce Task serialization --- pom.xml | 5 ++ server/pom.xml | 1 + server/task-json/pom.xml | 80 ++++++++++++++++++++ .../server/task/json/InvalidTaskTypeException.java | 22 ++++++ .../james/server/task/json/TaskDeserializer.java | 78 +++++++++++++++++++ .../james/server/task/json/TaskSerializer.java | 40 ++++++++++ .../server/task/json/UnsupportedTypeException.java | 32 ++++++++ .../server/task/json/TaskDeserializerTest.java | 88 ++++++++++++++++++++++ .../james/server/task/json/TaskSerializerTest.java | 37 +++++++++ .../apache/james/server/task/json/TestTask.java | 64 ++++++++++++++++ .../src/main/java/org/apache/james/task/Task.java | 6 ++ 11 files changed, 453 insertions(+) diff --git a/pom.xml b/pom.xml index 9eee488..2021201 100644 --- a/pom.xml +++ b/pom.xml @@ -1704,6 +1704,11 @@ </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>james-server-task-json</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>james-server-util</artifactId> <version>${project.version}</version> </dependency> diff --git a/server/pom.xml b/server/pom.xml index 0d82ea6..db5d711 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -104,6 +104,7 @@ <module>queue/queue-rabbitmq</module> <module>task</module> + <module>task-json</module> <module>testing</module> </modules> diff --git a/server/task-json/pom.xml b/server/task-json/pom.xml new file mode 100644 index 0000000..443edcb --- /dev/null +++ b/server/task-json/pom.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>james-server</artifactId> + <groupId>org.apache.james</groupId> + <version>3.4.0-SNAPSHOT</version> + </parent> + + <artifactId>james-server-task-json</artifactId> + <name>Apache James :: Server :: Task :: Json</name> + + <dependencies> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>apache-james-mailbox-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>apache-james-mailbox-api</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>james-server-task</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>net.javacrumbs.json-unit</groupId> + <artifactId>json-unit-assertj</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-launcher</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/server/task-json/src/main/java/org/apache/james/server/task/json/InvalidTaskTypeException.java b/server/task-json/src/main/java/org/apache/james/server/task/json/InvalidTaskTypeException.java new file mode 100644 index 0000000..8382937 --- /dev/null +++ b/server/task-json/src/main/java/org/apache/james/server/task/json/InvalidTaskTypeException.java @@ -0,0 +1,22 @@ +/**************************************************************** + * 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.server.task.json; + +public class InvalidTaskTypeException extends RuntimeException { +} diff --git a/server/task-json/src/main/java/org/apache/james/server/task/json/TaskDeserializer.java b/server/task-json/src/main/java/org/apache/james/server/task/json/TaskDeserializer.java new file mode 100644 index 0000000..335a2d1 --- /dev/null +++ b/server/task-json/src/main/java/org/apache/james/server/task/json/TaskDeserializer.java @@ -0,0 +1,78 @@ +package org.apache.james.server.task.json; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.apache.james.task.Task; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.MoreObjects; + +public class TaskDeserializer { + + private final ObjectMapper objectMapper; + + public interface Factory { + Task create(JsonNode parameters); + } + + public static class Type { + public static Type of(String typeName) { + return new Type(typeName); + } + + private final String typeName; + + private Type(String typeName) { + this.typeName = typeName; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Type) { + Type that = (Type) o; + + return Objects.equals(this.typeName, that.typeName); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(typeName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("typeName", typeName) + .toString(); + } + } + + private final Map<Type, Factory> registry; + + public TaskDeserializer(Map<Type, Factory> registry) { + this.registry = registry; + objectMapper = new ObjectMapper(); + } + + public Task deserialize(String taskAsString) throws IOException { + JsonNode taskAsJson = objectMapper.readTree(taskAsString); + JsonNode parameters = taskAsJson.get("parameters"); + + return getFactory(taskAsJson).create(parameters); + } + + private Factory getFactory(JsonNode taskAsJson) { + Type type = Optional.ofNullable(taskAsJson.get("type")) + .map(JsonNode::asText) + .map(Type::of) + .orElseThrow(() -> new InvalidTaskTypeException()); + return Optional.ofNullable(registry.get(type)) + .orElseThrow(() -> new UnsupportedTypeException(type)); + } +} diff --git a/server/task-json/src/main/java/org/apache/james/server/task/json/TaskSerializer.java b/server/task-json/src/main/java/org/apache/james/server/task/json/TaskSerializer.java new file mode 100644 index 0000000..ea39a08 --- /dev/null +++ b/server/task-json/src/main/java/org/apache/james/server/task/json/TaskSerializer.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.server.task.json; + +import org.apache.james.task.Task; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +public class TaskSerializer { + + private ObjectMapper objectMapper; + + public TaskSerializer() { + this.objectMapper = new ObjectMapper(); + } + + public JsonNode serialize(Task task) { + return JsonNodeFactory.instance.objectNode() + .put("type", task.type()) + .set("parameters", objectMapper.valueToTree(task.parameters())); + } +} diff --git a/server/task-json/src/main/java/org/apache/james/server/task/json/UnsupportedTypeException.java b/server/task-json/src/main/java/org/apache/james/server/task/json/UnsupportedTypeException.java new file mode 100644 index 0000000..498ee8c --- /dev/null +++ b/server/task-json/src/main/java/org/apache/james/server/task/json/UnsupportedTypeException.java @@ -0,0 +1,32 @@ +/**************************************************************** + * 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.server.task.json; + +public class UnsupportedTypeException extends RuntimeException { + private final TaskDeserializer.Type type; + + public UnsupportedTypeException(TaskDeserializer.Type type) { + this.type = type; + } + + @Override + public String getMessage() { + return "The type " + type + " is not registered when trying to deserialize it"; + } +} diff --git a/server/task-json/src/test/java/org/apache/james/server/task/json/TaskDeserializerTest.java b/server/task-json/src/test/java/org/apache/james/server/task/json/TaskDeserializerTest.java new file mode 100644 index 0000000..2698b69 --- /dev/null +++ b/server/task-json/src/test/java/org/apache/james/server/task/json/TaskDeserializerTest.java @@ -0,0 +1,88 @@ +/**************************************************************** + * 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.server.task.json; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; + +import org.apache.james.task.Task; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableMap; + +class TaskDeserializerTest { + + private static final String TASK_AS_STRING = "{" + + "\"type\": \"testTask\"," + + "\"parameters\": {\"parameter\": \"1\"}" + + "}"; + + private static final String UNREGISTERED_TASK_AS_STRING = "{" + + "\"type\": \"unknown\"," + + "\"parameters\": {\"parameter\": \"1\"}" + + "}"; + + private static final String TWO_TYPES_TASK_AS_STRING = "{" + + "\"type\": \"testTask\"," + + "\"type\": \"unknown\"," + + "\"parameters\": {\"parameter\": \"1\"}" + + "}"; + + private static final String MISSING_TASK_AS_STRING = "{" + + "\"parameters\": {\"parameter\": \"1\"}" + + "}"; + + private TaskDeserializer testee; + + @BeforeEach + void setUp() { + TestTask.Factory factory = new TestTask.Factory(); + ImmutableMap<TaskDeserializer.Type, TaskDeserializer.Factory> registry = ImmutableMap.of(TaskDeserializer.Type.of("testTask"), factory); + testee = new TaskDeserializer(registry); + } + + @Test + void shouldDeserializeTaskWithRegisteredType() throws IOException { + Task task = testee.deserialize(TASK_AS_STRING); + Assertions.assertThat(task).isInstanceOf(TestTask.class); + Assertions.assertThat(task.parameters()).isEqualTo(ImmutableMap.of("parameter", "1")); + } + + @Test + void shouldThrowWhenNotRegisteredType() { + assertThatThrownBy(() -> testee.deserialize(UNREGISTERED_TASK_AS_STRING)) + .isInstanceOf(UnsupportedTypeException.class); + } + + @Test + void shouldThrowWhenMissingType() { + assertThatThrownBy(() -> testee.deserialize(MISSING_TASK_AS_STRING)) + .isInstanceOf(InvalidTaskTypeException.class); + } + + @Disabled("Not supported yet, fixed later") + @Test + void shouldThrowWhenDuplicateType() { + assertThatThrownBy(() -> testee.deserialize(TWO_TYPES_TASK_AS_STRING)) + .isInstanceOf(InvalidTaskTypeException.class); + } + +} \ No newline at end of file diff --git a/server/task-json/src/test/java/org/apache/james/server/task/json/TaskSerializerTest.java b/server/task-json/src/test/java/org/apache/james/server/task/json/TaskSerializerTest.java new file mode 100644 index 0000000..12a3758 --- /dev/null +++ b/server/task-json/src/test/java/org/apache/james/server/task/json/TaskSerializerTest.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.server.task.json; + +import org.junit.jupiter.api.Test; + +import net.javacrumbs.jsonunit.assertj.JsonAssertions; + +class TaskSerializerTest { + + private static final String TASK_AS_STRING = "{" + + "\"type\": \"testTask\"," + + "\"parameters\": {\"parameter\": \"1\"}" + + "}"; + + @Test + void shouldSerializeTaskWithItsType() { + TaskSerializer testee = new TaskSerializer(); + long parameter = 1L; + TestTask task = new TestTask(parameter); + JsonAssertions.assertThatJson(testee.serialize(task).toString()).isEqualTo(TASK_AS_STRING); + } +} \ No newline at end of file diff --git a/server/task-json/src/test/java/org/apache/james/server/task/json/TestTask.java b/server/task-json/src/test/java/org/apache/james/server/task/json/TestTask.java new file mode 100644 index 0000000..4ea611f --- /dev/null +++ b/server/task-json/src/test/java/org/apache/james/server/task/json/TestTask.java @@ -0,0 +1,64 @@ +/**************************************************************** + * 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.server.task.json; + +import java.util.Map; +import java.util.Optional; + +import org.apache.james.task.Task; +import org.apache.james.task.TaskExecutionDetails; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableMap; + +public class TestTask implements Task { + + private final long parameter; + + public TestTask(long parameter) { + this.parameter = parameter; + } + + @Override + public Result run() throws InterruptedException { + return null; + } + + @Override + public String type() { + return "testTask"; + } + + @Override + public Map<String, String> parameters() { + return ImmutableMap.of("parameter", String.valueOf(parameter)); + } + + @Override + public Optional<TaskExecutionDetails.AdditionalInformation> details() { + return Optional.empty(); + } + + public static class Factory implements TaskDeserializer.Factory { + + @Override + public Task create(JsonNode parameters) { + long parameter = parameters.get("parameter").asLong(); + return new TestTask(parameter); + } + } +} \ No newline at end of file diff --git a/server/task/src/main/java/org/apache/james/task/Task.java b/server/task/src/main/java/org/apache/james/task/Task.java index 2edb39d..3a3368d 100644 --- a/server/task/src/main/java/org/apache/james/task/Task.java +++ b/server/task/src/main/java/org/apache/james/task/Task.java @@ -20,9 +20,11 @@ package org.apache.james.task; import java.util.Arrays; +import java.util.Map; import java.util.Optional; import java.util.stream.Stream; +import org.apache.commons.lang3.NotImplementedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -97,6 +99,10 @@ public interface Task { return UNKNOWN; } + default Map<String, String> parameters() { + throw new NotImplementedException("Tasks should implement 'parameters' to be serializable"); + } + default Optional<TaskExecutionDetails.AdditionalInformation> details() { return Optional.empty(); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
