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 ad...@apache.org on 2017/07/27 06:53:50 UTC
[3/9] james-project git commit: JAMES-2096 Introduce Cassandra
webadmin migration endpoints
JAMES-2096 Introduce Cassandra webadmin migration endpoints
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/f495121d
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/f495121d
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/f495121d
Branch: refs/heads/master
Commit: f495121dff2ac1c81215898cfff5ea9d25d6ad4b
Parents: 4f13da5
Author: Antoine Duprat <ad...@linagora.com>
Authored: Tue Jul 11 15:32:12 2017 +0200
Committer: benwa <bt...@linagora.com>
Committed: Tue Jul 25 17:51:01 2017 +0700
----------------------------------------------------------------------
server/pom.xml | 6 +
.../webadmin/webadmin-cassandra/pom.xml | 251 ++++++++++++++++++
.../webadmin/dto/CassandraVersionRequest.java | 40 +++
.../webadmin/dto/CassandraVersionResponse.java | 35 +++
.../routes/CassandraMigrationRoutes.java | 105 ++++++++
.../service/CassandraMigrationService.java | 98 +++++++
.../webadmin/service/MigrationException.java | 26 ++
.../james/webadmin/dto/VersionRequestTest.java | 67 +++++
.../routes/CassandraMigrationRoutesTest.java | 202 ++++++++++++++
.../service/CassandraMigrationServiceTest.java | 263 +++++++++++++++++++
10 files changed, 1093 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/pom.xml
----------------------------------------------------------------------
diff --git a/server/pom.xml b/server/pom.xml
index 4e52dae..2f90850 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -102,6 +102,7 @@
<module>protocols/protocols-managesieve</module>
<module>protocols/protocols-pop3</module>
<module>protocols/protocols-smtp</module>
+ <module>protocols/webadmin/webadmin-cassandra</module>
<module>protocols/webadmin/webadmin-core</module>
<module>protocols/webadmin/webadmin-data</module>
<module>protocols/webadmin/webadmin-mailbox</module>
@@ -679,6 +680,11 @@
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
+ <artifactId>james-server-webadmin-cassandra</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.james</groupId>
<artifactId>james-server-webadmin-core</artifactId>
<version>${project.version}</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/pom.xml b/server/protocols/webadmin/webadmin-cassandra/pom.xml
new file mode 100644
index 0000000..0d68ced
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/pom.xml
@@ -0,0 +1,251 @@
+<?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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>james-server</artifactId>
+ <groupId>org.apache.james</groupId>
+ <version>3.1.0-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>james-server-webadmin-cassandra</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Apache James :: Server :: Web Admin :: Cassandra</name>
+
+ <profiles>
+ <profile>
+ <id>noTest</id>
+ <activation>
+ <os>
+ <family>windows</family>
+ </os>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>disable-build-for-older-jdk</id>
+ <activation>
+ <jdk>(,1.8)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-jar</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>jar</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>test-jar</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-compile</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>default-testCompile</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-install-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-install</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-resources</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>default-testResources</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-site-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-descriptor</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>build-for-jdk-8</id>
+ <activation>
+ <jdk>[1.8,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.james</groupId>
+ <artifactId>apache-james-mailbox-cassandra</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.james</groupId>
+ <artifactId>james-server-webadmin-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.james</groupId>
+ <artifactId>metrics-logger</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.jayway.restassured</groupId>
+ <artifactId>rest-assured</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj-3.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>fully.qualified.MainClass</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>animal-sniffer-java-8</id>
+ <activation>
+ <jdk>[1.8,)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>animal-sniffer-maven-plugin</artifactId>
+ <configuration>
+ <signature>
+ <groupId>org.codehaus.mojo.signature</groupId>
+ <artifactId>java18</artifactId>
+ <version>1.0</version>
+ </signature>
+ </configuration>
+ <executions>
+ <execution>
+ <id>check_java_8</id>
+ <phase>test</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionRequest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionRequest.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionRequest.java
new file mode 100644
index 0000000..5eaef46
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionRequest.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 com.google.common.base.Preconditions;
+
+public class CassandraVersionRequest {
+ public static CassandraVersionRequest parse(String version) {
+ Preconditions.checkNotNull(version, "Version is mandatory");
+ return new CassandraVersionRequest(Integer.valueOf(version));
+ }
+
+ private final int value;
+
+ private CassandraVersionRequest(int value) {
+ Preconditions.checkArgument(value >= 0);
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionResponse.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionResponse.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionResponse.java
new file mode 100644
index 0000000..34e98a8
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/dto/CassandraVersionResponse.java
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.Optional;
+
+public class CassandraVersionResponse {
+
+ private final Optional<Integer> version;
+
+ public CassandraVersionResponse(Optional<Integer> version) {
+ this.version = version;
+ }
+
+ public Optional<Integer> getversion() {
+ return version;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/CassandraMigrationRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/CassandraMigrationRoutes.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/CassandraMigrationRoutes.java
new file mode 100644
index 0000000..c9564e7
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/CassandraMigrationRoutes.java
@@ -0,0 +1,105 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.webadmin.routes;
+
+import javax.inject.Inject;
+
+import org.apache.james.webadmin.Constants;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.dto.CassandraVersionRequest;
+import org.apache.james.webadmin.service.CassandraMigrationService;
+import org.apache.james.webadmin.service.MigrationException;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import spark.Service;
+
+public class CassandraMigrationRoutes implements Routes {
+
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CassandraMigrationRoutes.class);
+
+ public static final String VERSION_BASE = "/cassandra/version";
+ private static final String VERSION_BASE_LATEST = VERSION_BASE + "/latest";
+ private static final String VERSION_UPGRADE_BASE = VERSION_BASE + "/upgrade";
+ private static final String VERSION_UPGRADE_TO_LATEST_BASE = VERSION_UPGRADE_BASE + "/latest";
+ private static final int NO_CONTENT = 204;
+ private static final int INVALID_VERSION = 400;
+ private static final int MIGRATION_CAN_NOT_BE_PERFORMED = 410;
+ private static final int INTERNAL_ERROR = 500;
+
+ private final CassandraMigrationService cassandraMigrationService;
+ private final JsonTransformer jsonTransformer;
+
+ @Inject
+ public CassandraMigrationRoutes(CassandraMigrationService cassandraMigrationService, JsonTransformer jsonTransformer) {
+ this.cassandraMigrationService = cassandraMigrationService;
+ this.jsonTransformer = jsonTransformer;
+ }
+
+ @Override
+ public void define(Service service) {
+ service.get(VERSION_BASE,
+ (request, response) -> cassandraMigrationService.getCurrentVersion(),
+ jsonTransformer);
+
+ service.get(VERSION_BASE_LATEST,
+ (request, response) -> cassandraMigrationService.getLatestVersion(),
+ jsonTransformer);
+
+ service.post(VERSION_UPGRADE_BASE, (request, response) -> {
+ LOGGER.debug("Cassandra upgrade launched");
+ try {
+ CassandraVersionRequest cassandraVersionRequest = CassandraVersionRequest.parse(request.body());
+ cassandraMigrationService.upgradeToVersion(cassandraVersionRequest.getValue());
+ response.status(NO_CONTENT);
+ } catch (NullPointerException | IllegalArgumentException e) {
+ LOGGER.info("Invalid request for version upgrade");
+ response.status(INVALID_VERSION);
+ } catch (IllegalStateException e) {
+ LOGGER.info("The migration requested can not be performed.", e);
+ response.status(MIGRATION_CAN_NOT_BE_PERFORMED);
+ response.body(e.getMessage());
+ } catch (MigrationException e) {
+ LOGGER.error("An error lead to partial migration process", e);
+ response.status(INTERNAL_ERROR);
+ response.body(e.getMessage());
+ }
+ return Constants.EMPTY_BODY;
+ });
+
+ service.post(VERSION_UPGRADE_TO_LATEST_BASE, (request, response) -> {
+ try {
+ cassandraMigrationService.upgradeToLastVersion();
+ } catch (IllegalStateException e) {
+ LOGGER.info("The migration requested can not be performed.", e);
+ response.status(MIGRATION_CAN_NOT_BE_PERFORMED);
+ response.body(e.getMessage());
+ } catch (MigrationException e) {
+ LOGGER.error("An error lead to partial migration process", e);
+ response.status(INTERNAL_ERROR);
+ response.body(e.getMessage());
+ }
+
+ return Constants.EMPTY_BODY;
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/CassandraMigrationService.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/CassandraMigrationService.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/CassandraMigrationService.java
new file mode 100644
index 0000000..b18770f
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/CassandraMigrationService.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.service;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.IntStream;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
+import org.apache.james.mailbox.cassandra.mail.migration.Migration;
+import org.apache.james.webadmin.dto.CassandraVersionResponse;
+
+import com.google.common.base.Preconditions;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CassandraMigrationService {
+ private static final int FIRST_VERSION = 1;
+ public static final String LATEST_VERSION = "latestVersion";
+ private final CassandraSchemaVersionDAO schemaVersionDAO;
+ private final int latestVersion;
+ private final Map<Integer, Migration> allMigrationClazz;
+ private final Logger LOG = LoggerFactory.getLogger(CassandraMigrationService.class);
+
+ @Inject
+ public CassandraMigrationService(CassandraSchemaVersionDAO schemaVersionDAO, Map<Integer, Migration> allMigrationClazz, @Named(LATEST_VERSION) int latestVersion) {
+ Preconditions.checkArgument(latestVersion >= 0, "The latest version must be positive");
+ this.schemaVersionDAO = schemaVersionDAO;
+ this.latestVersion = latestVersion;
+ this.allMigrationClazz = allMigrationClazz;
+ }
+
+ public CassandraVersionResponse getCurrentVersion() {
+ return new CassandraVersionResponse(schemaVersionDAO.getCurrentSchemaVersion().join());
+ }
+
+ public CassandraVersionResponse getLatestVersion() {
+ return new CassandraVersionResponse(Optional.of(latestVersion));
+ }
+
+ public synchronized void upgradeToVersion(int newVersion) {
+ int currentVersion = schemaVersionDAO.getCurrentSchemaVersion().join().orElse(FIRST_VERSION);
+ if (currentVersion >= newVersion) {
+ throw new IllegalStateException("Current version is already up to date");
+ }
+
+ IntStream.range(currentVersion, newVersion)
+ .boxed()
+ .forEach(this::doMigration);
+ }
+
+ public void upgradeToLastVersion() {
+ upgradeToVersion(latestVersion);
+ }
+
+ private void doMigration(Integer version) {
+ if (allMigrationClazz.containsKey(version)) {
+ LOG.info("Migrating to version {} ", version + 1);
+ boolean migrationSuccess = allMigrationClazz.get(version).run();
+ if (migrationSuccess) {
+ schemaVersionDAO.updateVersion(version + 1);
+ LOG.info("Migrating to version {} done", version + 1);
+ } else {
+ String message = String.format("Migrating to version %d partially done. " +
+ "Please check logs for cause of failure and re-run this migration.",
+ version + 1);
+ LOG.warn(message);
+ throw new MigrationException(message);
+ }
+ } else {
+ String message = String.format("Can not migrate to %d. No migration class registered.", version + 1);
+ LOG.error(message);
+ throw new NotImplementedException(message);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/MigrationException.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/MigrationException.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/MigrationException.java
new file mode 100644
index 0000000..7b9d74f
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/service/MigrationException.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * 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.service;
+
+public class MigrationException extends RuntimeException {
+ public MigrationException(String message) {
+ super(message);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/dto/VersionRequestTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/dto/VersionRequestTest.java b/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/dto/VersionRequestTest.java
new file mode 100644
index 0000000..48ce134
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/dto/VersionRequestTest.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * 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 static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class VersionRequestTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void parseShouldThrowWhenNullVersion() throws Exception {
+ expectedException.expect(NullPointerException.class);
+
+ CassandraVersionRequest.parse(null);
+ }
+
+ @Test
+ public void parseShouldThrowWhenNonIntegerVersion() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+
+ CassandraVersionRequest.parse("NoInt");
+ }
+
+ @Test
+ public void parseShouldThrowWhenNegativeVersion() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+
+ CassandraVersionRequest.parse("-1");
+ }
+
+ @Test
+ public void parseShouldAcceptZeroVersion() throws Exception {
+ CassandraVersionRequest cassandraVersionRequest = CassandraVersionRequest.parse("0");
+
+ assertThat(cassandraVersionRequest.getValue()).isEqualTo(0);
+ }
+
+ @Test
+ public void parseShouldParseTheVersionValue() throws Exception {
+ CassandraVersionRequest cassandraVersionRequest = CassandraVersionRequest.parse("1");
+
+ assertThat(cassandraVersionRequest.getValue()).isEqualTo(1);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/routes/CassandraMigrationRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/routes/CassandraMigrationRoutesTest.java b/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/routes/CassandraMigrationRoutesTest.java
new file mode 100644
index 0000000..5944044
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/routes/CassandraMigrationRoutesTest.java
@@ -0,0 +1,202 @@
+/****************************************************************
+ * 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.config.EncoderConfig.encoderConfig;
+import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
+import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
+import static org.hamcrest.CoreMatchers.is;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
+import org.apache.james.mailbox.cassandra.mail.migration.Migration;
+import org.apache.james.metrics.logger.DefaultMetricFactory;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.service.CassandraMigrationService;
+import org.apache.james.webadmin.utils.JsonTransformer;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableMap;
+import com.jayway.restassured.RestAssured;
+import com.jayway.restassured.builder.RequestSpecBuilder;
+import com.jayway.restassured.http.ContentType;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class CassandraMigrationRoutesTest {
+
+ public static final boolean MIGRATED = true;
+ private static final Integer LATEST_VERSION = 3;
+ private static final Integer CURRENT_VERSION = 2;
+ private static final Integer OLDER_VERSION = 1;
+ private WebAdminServer webAdminServer;
+ private CassandraSchemaVersionDAO schemaVersionDAO;
+ private Migration successfulMigration;
+
+ private void createServer() throws Exception {
+ successfulMigration = mock(Migration.class);
+ when(successfulMigration.run()).thenReturn(MIGRATED);
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, successfulMigration)
+ .put(CURRENT_VERSION, successfulMigration)
+ .put(LATEST_VERSION, successfulMigration)
+ .build();
+ schemaVersionDAO = mock(CassandraSchemaVersionDAO.class);
+
+ webAdminServer = new WebAdminServer(
+ new DefaultMetricFactory(),
+ new CassandraMigrationRoutes(new CassandraMigrationService(schemaVersionDAO, allMigrationClazz, LATEST_VERSION), new JsonTransformer()));
+ webAdminServer.configure(NO_CONFIGURATION);
+ webAdminServer.await();
+
+ RestAssured.requestSpecification = new RequestSpecBuilder()
+ .setContentType(ContentType.JSON)
+ .setAccept(ContentType.JSON)
+ .setBasePath(CassandraMigrationRoutes.VERSION_BASE)
+ .setPort(webAdminServer.getPort().toInt())
+ .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)))
+ .build();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ createServer();
+ }
+
+ @After
+ public void tearDown() {
+ webAdminServer.destroy();
+ }
+
+ @Test
+ public void getShouldReturnTheCurrentVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(CURRENT_VERSION)));
+
+ when()
+ .get()
+ .then()
+ .statusCode(200)
+ .body(is("{\"version\":2}"));
+ }
+
+ @Test
+ public void getShouldReturnTheLatestVersionWhenSetUpTheLatestVersion() throws Exception {
+ when()
+ .get("/latest")
+ .then()
+ .statusCode(200)
+ .body(is("{\"version\":" + LATEST_VERSION + "}"));
+ }
+
+ @Ignore
+ @Test
+ public void postShouldReturnConflictWhenMigrationOnRunning() throws Exception {
+ when()
+ .post("/upgrade")
+ .then()
+ .statusCode(409);
+ }
+
+ @Test
+ public void postShouldReturnErrorCodeWhenInvalidVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ given()
+ .body(String.valueOf("NonInt"))
+ .with()
+ .post("/upgrade")
+ .then()
+ .statusCode(400);
+
+ verifyNoMoreInteractions(schemaVersionDAO);
+ }
+
+ @Test
+ public void postShouldDoMigrationToNewVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ given()
+ .body(String.valueOf(CURRENT_VERSION))
+ .with()
+ .post("/upgrade")
+ .then()
+ .statusCode(204);
+
+ verify(schemaVersionDAO, times(1)).getCurrentSchemaVersion();
+ verify(schemaVersionDAO, times(1)).updateVersion(eq(CURRENT_VERSION));
+ verifyNoMoreInteractions(schemaVersionDAO);
+ }
+
+ @Test
+ public void postShouldNotDoMigrationWhenCurrentVersionIsNewerThan() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(CURRENT_VERSION)));
+
+ given()
+ .body(String.valueOf(OLDER_VERSION))
+ .with()
+ .post("/upgrade")
+ .then()
+ .statusCode(410);
+
+ verify(schemaVersionDAO, times(1)).getCurrentSchemaVersion();
+ verifyNoMoreInteractions(schemaVersionDAO);
+ }
+
+ @Test
+ public void postShouldDoMigrationToLatestVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ when()
+ .post("/upgrade/latest")
+ .then()
+ .statusCode(200);
+
+ verify(schemaVersionDAO, times(1)).getCurrentSchemaVersion();
+ verify(schemaVersionDAO, times(1)).updateVersion(eq(CURRENT_VERSION));
+ verify(schemaVersionDAO, times(1)).updateVersion(eq(LATEST_VERSION));
+ verifyNoMoreInteractions(schemaVersionDAO);
+ }
+
+ @Test
+ public void postShouldNotDoMigrationToLatestVersionWhenItIsUpToDate() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(LATEST_VERSION)));
+
+ when()
+ .post("/upgrade/latest")
+ .then()
+ .statusCode(410);
+
+ verify(schemaVersionDAO, times(1)).getCurrentSchemaVersion();
+ verifyNoMoreInteractions(schemaVersionDAO);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f495121d/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/service/CassandraMigrationServiceTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/service/CassandraMigrationServiceTest.java b/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/service/CassandraMigrationServiceTest.java
new file mode 100644
index 0000000..7134e70
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/test/java/org/apache/james/webadmin/service/CassandraMigrationServiceTest.java
@@ -0,0 +1,263 @@
+/****************************************************************
+ * 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.service;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
+import org.apache.james.mailbox.cassandra.mail.migration.Migration;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.datastax.driver.core.Session;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+
+public class CassandraMigrationServiceTest {
+ private static final int LATEST_VERSION = 3;
+ private static final int CURRENT_VERSION = 2;
+ private static final int OLDER_VERSION = 1;
+ private static final boolean MIGRATED = true;
+ private static final boolean MIGRATION_FAILED = false;
+ private CassandraMigrationService testee;
+ private CassandraSchemaVersionDAO schemaVersionDAO;
+ private ExecutorService executorService;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ private Migration successfulMigration;
+
+ @Before
+ public void setUp() throws Exception {
+ schemaVersionDAO = mock(CassandraSchemaVersionDAO.class);
+ successfulMigration = mock(Migration.class);
+ when(successfulMigration.run()).thenReturn(MIGRATED);
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, successfulMigration)
+ .put(CURRENT_VERSION, successfulMigration)
+ .put(LATEST_VERSION, successfulMigration)
+ .build();
+ testee = new CassandraMigrationService(schemaVersionDAO, allMigrationClazz, LATEST_VERSION);
+ executorService = Executors.newFixedThreadPool(2);
+ }
+
+ @After
+ public void tearDown() {
+ executorService.shutdownNow();
+ }
+
+ @Test
+ public void getCurrentVersionShouldReturnCurrentVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(CURRENT_VERSION)));
+
+ assertThat(testee.getCurrentVersion().getversion().get()).isEqualTo(CURRENT_VERSION);
+ }
+
+ @Test
+ public void getLatestVersionShouldReturnTheLatestVersion() throws Exception {
+ assertThat(testee.getLatestVersion().getversion().get()).isEqualTo(LATEST_VERSION);
+ }
+
+ @Test
+ public void upgradeToVersionShouldThrowWhenCurrentVersionIsUpToDate() throws Exception {
+ expectedException.expect(IllegalStateException.class);
+
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(CURRENT_VERSION)));
+
+ testee.upgradeToVersion(OLDER_VERSION);
+ }
+
+ @Test
+ public void upgradeToVersionShouldUpdateToVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ testee.upgradeToVersion(CURRENT_VERSION);
+
+ verify(schemaVersionDAO, times(1)).updateVersion(eq(CURRENT_VERSION));
+ }
+
+ @Test
+ public void upgradeToLastVersionShouldThrowWhenVersionIsUpToDate() throws Exception {
+ expectedException.expect(IllegalStateException.class);
+
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(LATEST_VERSION)));
+
+ testee.upgradeToLastVersion();
+ }
+
+ @Test
+ public void upgradeToLastVersionShouldUpdateToLatestVersion() throws Exception {
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ testee.upgradeToLastVersion();
+
+ verify(schemaVersionDAO, times(1)).updateVersion(eq(LATEST_VERSION));
+ }
+
+ @Test
+ public void upgradeToVersionShouldThrowOnMissingVersion() throws Exception {
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, successfulMigration)
+ .put(LATEST_VERSION, successfulMigration)
+ .build();
+ testee = new CassandraMigrationService(schemaVersionDAO, allMigrationClazz, LATEST_VERSION);
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ expectedException.expect(NotImplementedException.class);
+
+ testee.upgradeToVersion(LATEST_VERSION);
+ }
+
+ @Test
+ public void upgradeToVersionShouldUpdateIntermediarySuccessfulMigrationsInCaseOfError() throws Exception {
+ try {
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, successfulMigration)
+ .put(LATEST_VERSION, successfulMigration)
+ .build();
+ testee = new CassandraMigrationService(schemaVersionDAO, allMigrationClazz, LATEST_VERSION);
+ when(schemaVersionDAO.getCurrentSchemaVersion()).thenReturn(CompletableFuture.completedFuture(Optional.of(OLDER_VERSION)));
+
+ expectedException.expect(RuntimeException.class);
+
+ testee.upgradeToVersion(LATEST_VERSION);
+ } finally {
+ verify(schemaVersionDAO).updateVersion(CURRENT_VERSION);
+ }
+ }
+
+ @Test
+ public void concurrentMigrationsShouldFail() throws Exception {
+ // Given a stateful migration service
+ Migration wait1SecondMigration = mock(Migration.class);
+ doAnswer(invocation -> {
+ Thread.sleep(1000);
+ return MIGRATED;
+ }).when(wait1SecondMigration).run();
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, wait1SecondMigration)
+ .put(CURRENT_VERSION, wait1SecondMigration)
+ .put(LATEST_VERSION, wait1SecondMigration)
+ .build();
+ testee = new CassandraMigrationService(new InMemorySchemaDAO(OLDER_VERSION), allMigrationClazz, LATEST_VERSION);
+
+ // When I perform a concurrent migration
+ AtomicInteger encounteredExceptionCount = new AtomicInteger(0);
+ executorService.submit(() -> testee.upgradeToVersion(LATEST_VERSION));
+ executorService.submit(() -> {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ throw Throwables.propagate(e);
+ }
+
+ try {
+ testee.upgradeToVersion(LATEST_VERSION);
+ } catch (IllegalStateException e) {
+ encounteredExceptionCount.incrementAndGet();
+ }
+ });
+ executorService.awaitTermination(10, TimeUnit.SECONDS);
+
+ // Then the second migration fails
+ assertThat(encounteredExceptionCount.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void partialMigrationShouldThrow() throws Exception {
+ Migration migration1 = mock(Migration.class);
+ when(migration1.run()).thenReturn(MIGRATION_FAILED);
+ Migration migration2 = successfulMigration;
+
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, migration1)
+ .put(CURRENT_VERSION, migration2)
+ .build();
+ testee = new CassandraMigrationService(new InMemorySchemaDAO(OLDER_VERSION), allMigrationClazz, LATEST_VERSION);
+
+ expectedException.expect(MigrationException.class);
+
+ testee.upgradeToVersion(LATEST_VERSION);
+ }
+
+ @Test
+ public void partialMigrationShouldAbortMigrations() throws Exception {
+ Migration migration1 = mock(Migration.class);
+ when(migration1.run()).thenReturn(MIGRATION_FAILED);
+ Migration migration2 = mock(Migration.class);
+ when(migration2.run()).thenReturn(MIGRATED);
+
+ Map<Integer, Migration> allMigrationClazz = ImmutableMap.<Integer, Migration>builder()
+ .put(OLDER_VERSION, migration1)
+ .put(CURRENT_VERSION, migration2)
+ .build();
+ testee = new CassandraMigrationService(new InMemorySchemaDAO(OLDER_VERSION), allMigrationClazz, LATEST_VERSION);
+
+ expectedException.expect(MigrationException.class);
+
+ try {
+ testee.upgradeToVersion(LATEST_VERSION);
+ } finally {
+ verify(migration1, times(1)).run();
+ verifyNoMoreInteractions(migration1);
+ verifyZeroInteractions(migration2);
+ }
+ }
+
+ public static class InMemorySchemaDAO extends CassandraSchemaVersionDAO {
+ private int currentVersion;
+
+ public InMemorySchemaDAO(int currentVersion) {
+ super(mock(Session.class), null);
+ this.currentVersion = currentVersion;
+ }
+
+ @Override
+ public CompletableFuture<Optional<Integer>> getCurrentSchemaVersion() {
+ return CompletableFuture.completedFuture(Optional.of(currentVersion));
+ }
+
+ @Override
+ public CompletableFuture<Void> updateVersion(int newVersion) {
+ currentVersion = newVersion;
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+}
\ 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