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 2016/04/07 05:10:13 UTC
[3/9] james-project git commit: JAMES-1617 Implement
CassandraSieveRepository
JAMES-1617 Implement CassandraSieveRepository
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/96dfc5d5
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/96dfc5d5
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/96dfc5d5
Branch: refs/heads/master
Commit: 96dfc5d59a634b9a478bd312e1f1b87fdbd4ba95
Parents: 5f995ab
Author: Benoit Tellier <bt...@apache.org>
Authored: Thu Feb 18 10:55:36 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Thu Apr 7 09:13:30 2016 +0700
----------------------------------------------------------------------
backends-common/cassandra/pom.xml | 5 +
.../cassandra/utils/CassandraAsyncExecutor.java | 66 ++++
.../cassandra/utils/CassandraUtils.java | 5 +-
.../sieverepository/api/SieveRepository.java | 2 +
.../sieve/cassandra/CassandraSieveDAO.java | 316 +++++++++++++++++++
.../cassandra/CassandraSieveRepository.java | 240 ++++++++++++++
.../CassandraSieveRepositoryModule.java | 97 ++++++
.../model/ScriptContentAndActivation.java | 39 +++
.../james/sieve/cassandra/model/SieveQuota.java | 51 +++
.../tables/CassandraSieveClusterQuotaTable.java | 29 ++
.../tables/CassandraSieveQuotaTable.java | 27 ++
.../tables/CassandraSieveSpaceTable.java | 27 ++
.../cassandra/tables/CassandraSieveTable.java | 31 ++
.../cassandra/CassandraSieveRepositoryTest.java | 82 +++++
.../sieve/cassandra/model/SieveQuotaTest.java | 59 ++++
.../lib/AbstractSieveRepositoryTest.java | 28 +-
16 files changed, 1096 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/backends-common/cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/pom.xml b/backends-common/cassandra/pom.xml
index b74080b..d8d158a 100644
--- a/backends-common/cassandra/pom.xml
+++ b/backends-common/cassandra/pom.xml
@@ -149,6 +149,11 @@
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
+ <groupId>net.javacrumbs.future-converter</groupId>
+ <artifactId>future-converter-java8-guava</artifactId>
+ <version>0.3.0</version>
+ </dependency>
+ <dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj-3.version}</version>
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java
new file mode 100644
index 0000000..4559b67
--- /dev/null
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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.backends.cassandra.utils;
+
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.Statement;
+
+import net.javacrumbs.futureconverter.java8guava.FutureConverter;
+
+public class CassandraAsyncExecutor {
+
+ private final Session session;
+
+ @Inject
+ public CassandraAsyncExecutor(Session session) {
+ this.session = session;
+ }
+
+ public CompletableFuture<ResultSet> execute(Statement statement) {
+ return FutureConverter.toCompletableFuture(session.executeAsync(statement));
+ }
+
+
+ public CompletableFuture<Boolean> executeReturnApplied(Statement statement) {
+ return FutureConverter.toCompletableFuture(session.executeAsync(statement))
+ .thenApply(ResultSet::one)
+ .thenApply(row -> row.getBool(CassandraConstants.LIGHTWEIGHT_TRANSACTION_APPLIED));
+ }
+
+ public CompletableFuture<Void> executeVoid(Statement statement) {
+ return FutureConverter.toCompletableFuture(session.executeAsync(statement))
+ .thenAccept(this::consume);
+ }
+
+ public CompletableFuture<Optional<Row>> executeSingleRow(Statement statement) {
+ return execute(statement)
+ .thenApply(ResultSet::one)
+ .thenApply(Optional::ofNullable);
+ }
+
+ private <U> void consume(U value) {}
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraUtils.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraUtils.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraUtils.java
index 40c44e4..a9916bc 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraUtils.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraUtils.java
@@ -19,11 +19,12 @@
package org.apache.james.backends.cassandra.utils;
-import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.Row;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+
public class CassandraUtils {
public static Stream<Row> convertToStream(ResultSet resultSet) {
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-api/src/main/java/org/apache/james/sieverepository/api/SieveRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-api/src/main/java/org/apache/james/sieverepository/api/SieveRepository.java b/server/data/data-api/src/main/java/org/apache/james/sieverepository/api/SieveRepository.java
index 8e0a999..9bdb55e 100644
--- a/server/data/data-api/src/main/java/org/apache/james/sieverepository/api/SieveRepository.java
+++ b/server/data/data-api/src/main/java/org/apache/james/sieverepository/api/SieveRepository.java
@@ -37,6 +37,8 @@ import org.joda.time.DateTime;
*/
public interface SieveRepository {
+ String NO_SCRIPT_NAME = "";
+
void haveSpace(String user, String name, long size) throws QuotaExceededException, StorageException;
/**
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveDAO.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveDAO.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveDAO.java
new file mode 100644
index 0000000..3ba2810
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveDAO.java
@@ -0,0 +1,316 @@
+/****************************************************************
+ * 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.sieve.cassandra;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.delete;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.incr;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.sieve.cassandra.model.ScriptContentAndActivation;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveClusterQuotaTable;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveQuotaTable;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveSpaceTable;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveTable;
+import org.apache.james.sieverepository.api.ScriptSummary;
+import org.joda.time.DateTime;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.querybuilder.Select;
+
+public class CassandraSieveDAO {
+
+ private final CassandraAsyncExecutor cassandraAsyncExecutor;
+ private final PreparedStatement insertScriptStatement;
+ private final PreparedStatement selectActiveScriptStatement;
+ private final PreparedStatement selectActiveScriptMetadataStatement;
+ private final PreparedStatement selectActiveScriptNameStatement;
+ private final PreparedStatement selectClusterQuotaStatement;
+ private final PreparedStatement selectScriptsStatement;
+ private final PreparedStatement selectScriptStatement;
+ private final PreparedStatement selectScriptMetadataStatement;
+ private final PreparedStatement selectSpaceUsedByUserStatement;
+ private final PreparedStatement selectUserQuotaStatement;
+ private final PreparedStatement updateClusterQuotaStatement;
+ private final PreparedStatement updateUserQuotaStatement;
+ private final PreparedStatement updateScriptActivationStatement;
+ private final PreparedStatement updateSpaceUsedStatement;
+ private final PreparedStatement deleteClusterQuotaStatement;
+ private final PreparedStatement deleteScriptStatement;
+ private final PreparedStatement deleteUserQuotaStatement;
+
+ @Inject
+ public CassandraSieveDAO(Session session) {
+ this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+
+ insertScriptStatement = session.prepare(
+ insertInto(CassandraSieveTable.TABLE_NAME)
+ .value(CassandraSieveTable.USER_NAME, bindMarker(CassandraSieveTable.USER_NAME))
+ .value(CassandraSieveTable.SCRIPT_NAME, bindMarker(CassandraSieveTable.SCRIPT_NAME))
+ .value(CassandraSieveTable.SCRIPT_CONTENT, bindMarker(CassandraSieveTable.SCRIPT_CONTENT))
+ .value(CassandraSieveTable.IS_ACTIVE, bindMarker(CassandraSieveTable.IS_ACTIVE))
+ .value(CassandraSieveTable.SIZE, bindMarker(CassandraSieveTable.SIZE))
+ .value(CassandraSieveTable.DATE, bindMarker(CassandraSieveTable.DATE)));
+
+ selectActiveScriptStatement = session.prepare(getScriptQuery(CassandraSieveTable.SCRIPT_CONTENT, CassandraSieveTable.DATE)
+ .and(eq(CassandraSieveTable.IS_ACTIVE, bindMarker(CassandraSieveTable.IS_ACTIVE))));
+
+ selectActiveScriptMetadataStatement = session.prepare(getScriptQuery(CassandraSieveTable.DATE)
+ .and(eq(CassandraSieveTable.IS_ACTIVE, bindMarker(CassandraSieveTable.IS_ACTIVE))));
+
+ selectActiveScriptNameStatement = session.prepare(getScriptQuery(CassandraSieveTable.SCRIPT_NAME)
+ .and(eq(CassandraSieveTable.IS_ACTIVE, bindMarker(CassandraSieveTable.IS_ACTIVE))));
+
+ selectClusterQuotaStatement = session.prepare(
+ select(CassandraSieveClusterQuotaTable.VALUE)
+ .from(CassandraSieveClusterQuotaTable.TABLE_NAME)
+ .where(eq(CassandraSieveClusterQuotaTable.NAME, bindMarker(CassandraSieveClusterQuotaTable.NAME))));
+
+ selectScriptsStatement = session.prepare(
+ select()
+ .from(CassandraSieveTable.TABLE_NAME)
+ .where(eq(CassandraSieveTable.USER_NAME, bindMarker(CassandraSieveTable.USER_NAME))));
+
+ selectScriptStatement = session.prepare(getScriptQuery(CassandraSieveTable.SCRIPT_CONTENT, CassandraSieveTable.IS_ACTIVE)
+ .and(eq(CassandraSieveTable.SCRIPT_NAME, bindMarker(CassandraSieveTable.SCRIPT_NAME))));
+
+ selectScriptMetadataStatement = session.prepare(getScriptQuery(CassandraSieveTable.SIZE, CassandraSieveTable.IS_ACTIVE, CassandraSieveTable.DATE)
+ .and(eq(CassandraSieveTable.SCRIPT_NAME, bindMarker(CassandraSieveTable.SCRIPT_NAME))));
+
+ selectSpaceUsedByUserStatement = session.prepare(
+ select(CassandraSieveSpaceTable.SPACE_USED)
+ .from(CassandraSieveSpaceTable.TABLE_NAME)
+ .where(eq(CassandraSieveSpaceTable.USER_NAME, bindMarker(CassandraSieveSpaceTable.USER_NAME))));
+
+ selectUserQuotaStatement = session.prepare(
+ select(CassandraSieveQuotaTable.QUOTA)
+ .from(CassandraSieveQuotaTable.TABLE_NAME)
+ .where(eq(CassandraSieveQuotaTable.USER_NAME, bindMarker(CassandraSieveQuotaTable.USER_NAME))));
+
+ updateClusterQuotaStatement = session.prepare(
+ update(CassandraSieveClusterQuotaTable.TABLE_NAME)
+ .with(set(CassandraSieveClusterQuotaTable.VALUE, bindMarker(CassandraSieveClusterQuotaTable.VALUE)))
+ .where(eq(CassandraSieveClusterQuotaTable.NAME, bindMarker(CassandraSieveClusterQuotaTable.NAME))));
+
+ updateScriptActivationStatement = session.prepare(
+ update(CassandraSieveTable.TABLE_NAME)
+ .with(set(CassandraSieveTable.IS_ACTIVE, bindMarker(CassandraSieveTable.IS_ACTIVE)))
+ .where(eq(CassandraSieveTable.USER_NAME, bindMarker(CassandraSieveTable.USER_NAME)))
+ .and(eq(CassandraSieveTable.SCRIPT_NAME, bindMarker(CassandraSieveTable.SCRIPT_NAME)))
+ .ifExists());
+
+ updateSpaceUsedStatement = session.prepare(
+ update(CassandraSieveSpaceTable.TABLE_NAME)
+ .with(incr(CassandraSieveSpaceTable.SPACE_USED, bindMarker(CassandraSieveSpaceTable.SPACE_USED)))
+ .where(eq(CassandraSieveSpaceTable.USER_NAME, bindMarker(CassandraSieveSpaceTable.USER_NAME))));
+
+ updateUserQuotaStatement = session.prepare(
+ update(CassandraSieveQuotaTable.TABLE_NAME)
+ .with(set(CassandraSieveQuotaTable.QUOTA, bindMarker(CassandraSieveQuotaTable.QUOTA)))
+ .where(eq(CassandraSieveQuotaTable.USER_NAME, bindMarker(CassandraSieveQuotaTable.USER_NAME))));
+
+ deleteScriptStatement = session.prepare(
+ delete()
+ .from(CassandraSieveTable.TABLE_NAME)
+ .where(eq(CassandraSieveTable.USER_NAME, bindMarker(CassandraSieveTable.USER_NAME)))
+ .and(eq(CassandraSieveTable.SCRIPT_NAME, bindMarker(CassandraSieveTable.SCRIPT_NAME)))
+ .ifExists());
+
+ deleteClusterQuotaStatement = session.prepare(
+ delete()
+ .from(CassandraSieveClusterQuotaTable.TABLE_NAME)
+ .where(eq(CassandraSieveClusterQuotaTable.NAME, bindMarker(CassandraSieveClusterQuotaTable.NAME)))
+ .ifExists());
+
+ deleteUserQuotaStatement = session.prepare(
+ delete()
+ .from(CassandraSieveQuotaTable.TABLE_NAME)
+ .where(eq(CassandraSieveQuotaTable.USER_NAME, bindMarker(CassandraSieveQuotaTable.USER_NAME)))
+ .ifExists());
+ }
+
+ private Select.Where getScriptQuery(String... selectedRows) {
+ return select(selectedRows)
+ .from(CassandraSieveTable.TABLE_NAME)
+ .where(eq(CassandraSieveTable.USER_NAME, bindMarker(CassandraSieveTable.USER_NAME)));
+ }
+
+ public CompletableFuture<Long> spaceUsedBy(String user) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectSpaceUsedByUserStatement.bind()
+ .setString(CassandraSieveSpaceTable.USER_NAME, user))
+ .thenApply(optional -> optional.map(row -> row.getLong(CassandraSieveSpaceTable.SPACE_USED))
+ .orElse(0L));
+ }
+
+ public CompletableFuture<Void> insertScript(String user, String name, String content, boolean isActive) {
+ return cassandraAsyncExecutor.executeVoid(
+ insertScriptStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setString(CassandraSieveTable.SCRIPT_NAME, name)
+ .setString(CassandraSieveTable.SCRIPT_CONTENT, content)
+ .setBool(CassandraSieveTable.IS_ACTIVE, isActive)
+ .setLong(CassandraSieveTable.SIZE, content.getBytes().length)
+ .setDate(CassandraSieveTable.DATE, new Date()));
+ }
+
+ public CompletableFuture<List<ScriptSummary>> listScripts(String user) {
+ return cassandraAsyncExecutor.execute(
+ selectScriptsStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user))
+ .thenApply(resultSet -> resultSet.all()
+ .stream()
+ .map(row -> new ScriptSummary(
+ row.getString(CassandraSieveTable.SCRIPT_NAME),
+ row.getBool(CassandraSieveTable.IS_ACTIVE)))
+ .collect(Collectors.toList()));
+ }
+
+ public CompletableFuture<Void> updateSpaceUsed(String user, long spaceUsed) {
+ return cassandraAsyncExecutor.executeVoid(
+ updateSpaceUsedStatement.bind()
+ .setLong(CassandraSieveSpaceTable.SPACE_USED, spaceUsed)
+ .setString(CassandraSieveSpaceTable.USER_NAME, user));
+ }
+
+ public CompletableFuture<Boolean> updateScriptActivation(String user, String scriptName, boolean active) {
+ return cassandraAsyncExecutor.executeReturnApplied(
+ updateScriptActivationStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setString(CassandraSieveTable.SCRIPT_NAME, scriptName)
+ .setBool(CassandraSieveTable.IS_ACTIVE, active));
+ }
+
+ public CompletableFuture<Optional<ScriptContentAndActivation>> getScriptContentAndActivation(String user, String name) {
+ return getScriptRow(user, name).thenApply(opt -> opt.map(row -> new ScriptContentAndActivation(
+ row.getString(CassandraSieveTable.SCRIPT_CONTENT),
+ row.getBool(CassandraSieveTable.IS_ACTIVE))));
+ }
+
+ public CompletableFuture<Optional<String>> getScriptContent(String user, String name) {
+ return getScriptRow(user, name).thenApply(opt -> opt.map(row -> row.getString(CassandraSieveTable.SCRIPT_CONTENT)));
+ }
+
+ public CompletableFuture<Optional<Long>> getScriptSize(String user, String name) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectScriptMetadataStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setString(CassandraSieveTable.SCRIPT_NAME, name))
+ .thenApply(rowOptional -> rowOptional.map(row -> row.getLong(CassandraSieveTable.SIZE)));
+ }
+
+ public CompletableFuture<Optional<DateTime>> getActiveScriptActivationDate(String user) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectActiveScriptMetadataStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setBool(CassandraSieveTable.IS_ACTIVE, true))
+ .thenApply(rowOptional -> rowOptional.map(row -> new DateTime(row.getDate(CassandraSieveTable.DATE).getTime())));
+ }
+
+ public CompletableFuture<Boolean> deleteScriptInCassandra(String user, String name) {
+ return cassandraAsyncExecutor.executeReturnApplied(
+ deleteScriptStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setString(CassandraSieveTable.SCRIPT_NAME, name));
+ }
+
+ public CompletableFuture<Boolean> scriptExists(String user, String name) {
+ return getScriptSize(user, name).thenApply(Optional::isPresent);
+ }
+
+ public CompletableFuture<Optional<Long>> getQuota() {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectClusterQuotaStatement.bind()
+ .setString(CassandraSieveClusterQuotaTable.NAME, CassandraSieveClusterQuotaTable.DEFAULT_NAME))
+ .thenApply(optional -> optional.map(row -> row.getLong(CassandraSieveClusterQuotaTable.VALUE)));
+ }
+
+ public CompletableFuture<Void> setQuota(long quota) {
+ return cassandraAsyncExecutor.executeVoid(
+ updateClusterQuotaStatement.bind()
+ .setLong(CassandraSieveClusterQuotaTable.VALUE, quota)
+ .setString(CassandraSieveClusterQuotaTable.NAME, CassandraSieveClusterQuotaTable.DEFAULT_NAME));
+ }
+
+ public CompletableFuture<Boolean> removeQuota() {
+ return cassandraAsyncExecutor.executeReturnApplied(
+ deleteClusterQuotaStatement.bind()
+ .setString(CassandraSieveClusterQuotaTable.NAME, CassandraSieveClusterQuotaTable.DEFAULT_NAME));
+ }
+
+ public CompletableFuture<Optional<Long>> getQuota(String user) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectUserQuotaStatement.bind()
+ .setString(CassandraSieveQuotaTable.USER_NAME, user))
+ .thenApply(optional -> optional.map(row -> row.getLong(CassandraSieveQuotaTable.QUOTA)));
+ }
+
+ public CompletableFuture<Optional<String>> getActiveName(String user) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectActiveScriptNameStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setBool(CassandraSieveTable.IS_ACTIVE, true))
+ .thenApply(optional -> optional.map(row -> row.getString(CassandraSieveTable.SCRIPT_NAME)));
+ }
+
+ public CompletableFuture<Void> setQuota(String user, long quota) {
+ return cassandraAsyncExecutor.executeVoid(
+ updateUserQuotaStatement.bind()
+ .setLong(CassandraSieveQuotaTable.QUOTA, quota)
+ .setString(CassandraSieveQuotaTable.USER_NAME, user));
+ }
+
+ public CompletableFuture<Boolean> removeQuota(String user) {
+ return cassandraAsyncExecutor.executeReturnApplied(
+ deleteUserQuotaStatement.bind()
+ .setString(CassandraSieveQuotaTable.USER_NAME, user));
+ }
+
+ public CompletableFuture<Optional<String>> getActive(String user) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectActiveScriptStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setBool(CassandraSieveTable.IS_ACTIVE, true))
+ .thenApply(rowOptional -> rowOptional.map(row -> row.getString(CassandraSieveTable.SCRIPT_CONTENT)));
+ }
+
+ private CompletableFuture<Optional<Row>> getScriptRow(String user, String name) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ selectScriptStatement.bind()
+ .setString(CassandraSieveTable.USER_NAME, user)
+ .setString(CassandraSieveTable.SCRIPT_NAME, name));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java
new file mode 100644
index 0000000..2a36a91
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java
@@ -0,0 +1,240 @@
+/****************************************************************
+ * 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.sieve.cassandra;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.sieve.cassandra.model.ScriptContentAndActivation;
+import org.apache.james.sieverepository.api.ScriptSummary;
+import org.apache.james.sieve.cassandra.model.SieveQuota;
+import org.apache.james.sieverepository.api.SieveRepository;
+import org.apache.james.sieverepository.api.exception.DuplicateException;
+import org.apache.james.sieverepository.api.exception.IsActiveException;
+import org.apache.james.sieverepository.api.exception.QuotaExceededException;
+import org.apache.james.sieverepository.api.exception.QuotaNotFoundException;
+import org.apache.james.sieverepository.api.exception.ScriptNotFoundException;
+import org.apache.james.sieverepository.api.exception.StorageException;
+import org.joda.time.DateTime;
+
+public class CassandraSieveRepository implements SieveRepository {
+
+ private final CassandraSieveDAO cassandraSieveDAO;
+
+ @Inject
+ public CassandraSieveRepository(CassandraSieveDAO cassandraSieveDAO) {
+ this.cassandraSieveDAO = cassandraSieveDAO;
+ }
+
+ @Override
+ public DateTime getActivationDateForActiveScript(String user) throws StorageException, ScriptNotFoundException {
+ return cassandraSieveDAO.getActiveScriptActivationDate(user)
+ .join()
+ .orElseThrow(ScriptNotFoundException::new);
+ }
+
+ @Override
+ public void haveSpace(String user, String name, long newSize) throws QuotaExceededException, StorageException {
+ throwOnOverQuota(user, spaceThatWillBeUsedByNewScript(user, name, newSize));
+ }
+
+ private void throwOnOverQuota(String user, CompletableFuture<Long> sizeDifference) throws QuotaExceededException, StorageException {
+ CompletableFuture<Optional<Long>> userQuotaFuture = cassandraSieveDAO.getQuota(user);
+ CompletableFuture<Optional<Long>> globalQuotaFuture = cassandraSieveDAO.getQuota();
+ CompletableFuture<Long> spaceUsedFuture = cassandraSieveDAO.spaceUsedBy(user);
+
+ new SieveQuota(spaceUsedFuture.join(), limitToUse(userQuotaFuture, globalQuotaFuture)).checkOverQuotaUponModification(sizeDifference.join());
+ }
+
+ public CompletableFuture<Long> spaceThatWillBeUsedByNewScript(String user, String name, long scriptSize) {
+ return cassandraSieveDAO.getScriptSize(user, name)
+ .thenApply(sizeOfStoredScript -> scriptSize - sizeOfStoredScript.orElse(0L));
+ }
+
+ private Optional<Long> limitToUse(CompletableFuture<Optional<Long>> userQuota, CompletableFuture<Optional<Long>> globalQuota) {
+ if (userQuota.join().isPresent()) {
+ return userQuota.join();
+ }
+ return globalQuota.join();
+ }
+
+ @Override
+ public void putScript(String user, String name, String content) throws QuotaExceededException, StorageException {
+ CompletableFuture<Long> spaceUsed = spaceThatWillBeUsedByNewScript(user, name, content.length());
+ throwOnOverQuota(user, spaceUsed);
+
+ CompletableFuture.allOf(
+ updateSpaceUsed(user, spaceUsed.join()),
+ cassandraSieveDAO.insertScript(user, name, content, false))
+ .join();
+ }
+
+ public CompletableFuture<Void> updateSpaceUsed(String user, long spaceUsed) {
+ if (spaceUsed == 0) {
+ return CompletableFuture.completedFuture(null);
+ }
+ return cassandraSieveDAO.updateSpaceUsed(user, spaceUsed);
+ }
+
+ @Override
+ public List<ScriptSummary> listScripts(String user) {
+ return cassandraSieveDAO.listScripts(user).join();
+ }
+
+ @Override
+ public InputStream getActive(String user) throws ScriptNotFoundException {
+ return IOUtils.toInputStream(
+ cassandraSieveDAO.getActive(user)
+ .join()
+ .orElseThrow(ScriptNotFoundException::new));
+ }
+
+ @Override
+ public void setActive(String user, String name) throws ScriptNotFoundException {
+ CompletableFuture<Void> unactivateOldScriptFuture = unactivateOldScript(user);
+ CompletableFuture<Boolean> activateNewScript = updateScriptActivation(user, name, true);
+
+ unactivateOldScriptFuture.join();
+ if (!activateNewScript.join()) {
+ throw new ScriptNotFoundException();
+ }
+ }
+
+ private CompletableFuture<Void> unactivateOldScript(String user) {
+ return cassandraSieveDAO.getActiveName(user)
+ .thenCompose(scriptNameOptional -> scriptNameOptional
+ .map(scriptName -> updateScriptActivation(user, scriptName, false)
+ .<Void>thenApply(any -> null))
+ .orElse(CompletableFuture.completedFuture(null)));
+ }
+
+ private CompletableFuture<Boolean> updateScriptActivation(String user, String scriptName, boolean active) {
+ if (!scriptName.equals(SieveRepository.NO_SCRIPT_NAME)) {
+ return cassandraSieveDAO.updateScriptActivation(user, scriptName, active);
+ }
+ return CompletableFuture.completedFuture(true);
+ }
+
+ @Override
+ public InputStream getScript(String user, String name) throws ScriptNotFoundException {
+ return cassandraSieveDAO.getScriptContent(user, name)
+ .join()
+ .map(IOUtils::toInputStream)
+ .orElseThrow(ScriptNotFoundException::new);
+ }
+
+ @Override
+ public void deleteScript(String user, String name) throws ScriptNotFoundException, IsActiveException {
+ ensureIsNotActive(user, name);
+ if (!cassandraSieveDAO.deleteScriptInCassandra(user, name).join()) {
+ throw new ScriptNotFoundException();
+ }
+ }
+
+ private void ensureIsNotActive(String user, String name) throws IsActiveException {
+ Optional<String> activeName = cassandraSieveDAO.getActiveName(user).join();
+ if (activeName.isPresent() && name.equals(activeName.get())) {
+ throw new IsActiveException();
+ }
+ }
+
+ @Override
+ public void renameScript(String user, String oldName, String newName) throws ScriptNotFoundException, DuplicateException {
+ CompletableFuture<Boolean> scriptExistsFuture = cassandraSieveDAO.scriptExists(user, newName);
+ CompletableFuture<Optional<ScriptContentAndActivation>> oldScriptFuture = cassandraSieveDAO.getScriptContentAndActivation(user, oldName);
+
+ oldScriptFuture.join();
+ if (scriptExistsFuture.join()) {
+ throw new DuplicateException();
+ }
+
+ performScriptRename(user,
+ oldName,
+ newName,
+ oldScriptFuture.join().orElseThrow(ScriptNotFoundException::new));
+ }
+
+ private void performScriptRename(String user, String oldName, String newName, ScriptContentAndActivation oldScript) {
+ CompletableFuture.allOf(
+ cassandraSieveDAO.insertScript(user, newName, oldScript.getContent(), oldScript.isActive()),
+ cassandraSieveDAO.deleteScriptInCassandra(user, oldName))
+ .join();
+ }
+
+ @Override
+ public boolean hasQuota() {
+ return cassandraSieveDAO.getQuota()
+ .join()
+ .isPresent();
+ }
+
+ @Override
+ public long getQuota() throws QuotaNotFoundException {
+ return cassandraSieveDAO.getQuota()
+ .join()
+ .orElseThrow(QuotaNotFoundException::new);
+ }
+
+ @Override
+ public void setQuota(long quota) {
+ cassandraSieveDAO.setQuota(quota).join();
+ }
+
+ @Override
+ public void removeQuota() throws QuotaNotFoundException {
+ if (!cassandraSieveDAO.removeQuota().join()) {
+ throw new QuotaNotFoundException();
+ }
+ }
+
+ @Override
+ public boolean hasQuota(String user) {
+ CompletableFuture<Boolean> userQuotaIsPresent = cassandraSieveDAO.getQuota(user).thenApply(Optional::isPresent);
+ CompletableFuture<Boolean> globalQuotaIsPresent = cassandraSieveDAO.getQuota().thenApply(Optional::isPresent);
+ CompletableFuture.allOf(userQuotaIsPresent, globalQuotaIsPresent).join();
+
+ return userQuotaIsPresent.join() || globalQuotaIsPresent.join();
+ }
+
+ @Override
+ public long getQuota(String user) throws QuotaNotFoundException {
+ return cassandraSieveDAO.getQuota(user)
+ .join()
+ .orElseThrow(QuotaNotFoundException::new);
+ }
+
+ @Override
+ public void setQuota(String user, long quota) {
+ cassandraSieveDAO.setQuota(user, quota).join();
+ }
+
+ @Override
+ public void removeQuota(String user) throws QuotaNotFoundException {
+ if (!cassandraSieveDAO.removeQuota(user).join()) {
+ throw new QuotaNotFoundException();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryModule.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryModule.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryModule.java
new file mode 100644
index 0000000..c59053d
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryModule.java
@@ -0,0 +1,97 @@
+/****************************************************************
+ * 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.sieve.cassandra;
+
+import static com.datastax.driver.core.DataType.bigint;
+import static com.datastax.driver.core.DataType.cboolean;
+import static com.datastax.driver.core.DataType.counter;
+import static com.datastax.driver.core.DataType.text;
+import static com.datastax.driver.core.DataType.timestamp;
+
+import java.util.List;
+
+import org.apache.james.backends.cassandra.components.CassandraIndex;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.components.CassandraTable;
+import org.apache.james.backends.cassandra.components.CassandraType;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveClusterQuotaTable;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveQuotaTable;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveSpaceTable;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveTable;
+
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import com.google.common.collect.ImmutableList;
+
+public class CassandraSieveRepositoryModule implements CassandraModule {
+
+ private final List<CassandraTable> tables;
+ private final List<CassandraIndex> index;
+ private final List<CassandraType> types;
+
+ public CassandraSieveRepositoryModule() {
+ tables = ImmutableList.of(
+ new CassandraTable(CassandraSieveTable.TABLE_NAME,
+ SchemaBuilder.createTable(CassandraSieveTable.TABLE_NAME)
+ .ifNotExists()
+ .addPartitionKey(CassandraSieveTable.USER_NAME, text())
+ .addClusteringColumn(CassandraSieveTable.SCRIPT_NAME, text())
+ .addColumn(CassandraSieveTable.SCRIPT_CONTENT, text())
+ .addColumn(CassandraSieveTable.IS_ACTIVE, cboolean())
+ .addColumn(CassandraSieveTable.DATE, timestamp())
+ .addColumn(CassandraSieveTable.SIZE, bigint())),
+ new CassandraTable(CassandraSieveSpaceTable.TABLE_NAME,
+ SchemaBuilder.createTable(CassandraSieveSpaceTable.TABLE_NAME)
+ .ifNotExists()
+ .addPartitionKey(CassandraSieveSpaceTable.USER_NAME, text())
+ .addColumn(CassandraSieveSpaceTable.SPACE_USED, counter())),
+ new CassandraTable(CassandraSieveQuotaTable.TABLE_NAME,
+ SchemaBuilder.createTable(CassandraSieveQuotaTable.TABLE_NAME)
+ .ifNotExists()
+ .addPartitionKey(CassandraSieveQuotaTable.USER_NAME, text())
+ .addColumn(CassandraSieveQuotaTable.QUOTA, bigint())),
+ new CassandraTable(CassandraSieveClusterQuotaTable.TABLE_NAME,
+ SchemaBuilder.createTable(CassandraSieveClusterQuotaTable.TABLE_NAME)
+ .ifNotExists()
+ .addPartitionKey(CassandraSieveClusterQuotaTable.NAME, text())
+ .addColumn(CassandraSieveClusterQuotaTable.VALUE, bigint())));
+ index = ImmutableList.of(
+ new CassandraIndex(
+ SchemaBuilder.createIndex(CassandraIndex.INDEX_PREFIX + CassandraSieveTable.TABLE_NAME + CassandraSieveTable.IS_ACTIVE)
+ .ifNotExists()
+ .onTable(CassandraSieveTable.TABLE_NAME)
+ .andColumn(CassandraSieveTable.IS_ACTIVE)));
+ types = ImmutableList.of();
+ }
+
+ @Override
+ public List<CassandraTable> moduleTables() {
+ return tables;
+ }
+
+ @Override
+ public List<CassandraIndex> moduleIndex() {
+ return index;
+ }
+
+ @Override
+ public List<CassandraType> moduleTypes() {
+ return types;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/ScriptContentAndActivation.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/ScriptContentAndActivation.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/ScriptContentAndActivation.java
new file mode 100644
index 0000000..0e26b7b
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/ScriptContentAndActivation.java
@@ -0,0 +1,39 @@
+/****************************************************************
+ * 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.sieve.cassandra.model;
+
+public class ScriptContentAndActivation {
+
+ private final String content;
+ private final boolean activation;
+
+ public ScriptContentAndActivation(String content, boolean activation) {
+ this.content = content;
+ this.activation = activation;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public boolean isActive() {
+ return activation;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/SieveQuota.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/SieveQuota.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/SieveQuota.java
new file mode 100644
index 0000000..c1a44d6
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/model/SieveQuota.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sieve.cassandra.model;
+
+import java.util.Optional;
+
+import org.apache.james.sieverepository.api.exception.QuotaExceededException;
+
+import com.google.common.base.Preconditions;
+
+public class SieveQuota {
+
+ private final long currentUsage;
+ private final Optional<Long> limit;
+
+ public SieveQuota(long currentUsage, Optional<Long> limit) {
+ Preconditions.checkArgument(currentUsage >= 0, "Current usage should be positive or equal to zero");
+ limit.ifPresent(limitValue -> Preconditions.checkArgument(limitValue >= 0, "Limit value should be positive or equal to zero"));
+ this.currentUsage = currentUsage;
+ this.limit = limit;
+ }
+
+ public void checkOverQuotaUponModification(long sizeDifference) throws QuotaExceededException {
+ if (isExceededUponModification(sizeDifference)) {
+ throw new QuotaExceededException();
+ }
+ }
+
+ public boolean isExceededUponModification(long sizeDifference) {
+ return limit.map(limitContent -> currentUsage + sizeDifference > limitContent)
+ .orElse(false);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveClusterQuotaTable.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveClusterQuotaTable.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveClusterQuotaTable.java
new file mode 100644
index 0000000..16beb0f
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveClusterQuotaTable.java
@@ -0,0 +1,29 @@
+/****************************************************************
+ * 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.sieve.cassandra.tables;
+
+public interface CassandraSieveClusterQuotaTable {
+ String TABLE_NAME = "sieve_cluster_quota";
+
+ String NAME = "name";
+ String VALUE = "value";
+
+ String DEFAULT_NAME = "cluster_quota";
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveQuotaTable.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveQuotaTable.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveQuotaTable.java
new file mode 100644
index 0000000..94c6425
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveQuotaTable.java
@@ -0,0 +1,27 @@
+/****************************************************************
+ * 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.sieve.cassandra.tables;
+
+public interface CassandraSieveQuotaTable {
+ String TABLE_NAME = "sieve_quota";
+
+ String USER_NAME = "user_name";
+ String QUOTA = "quota";
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveSpaceTable.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveSpaceTable.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveSpaceTable.java
new file mode 100644
index 0000000..8156e77
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveSpaceTable.java
@@ -0,0 +1,27 @@
+/****************************************************************
+ * 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.sieve.cassandra.tables;
+
+public interface CassandraSieveSpaceTable {
+ String TABLE_NAME = "sieve_space";
+
+ String USER_NAME = "user_name";
+ String SPACE_USED = "space_used";
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveTable.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveTable.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveTable.java
new file mode 100644
index 0000000..9479ea3
--- /dev/null
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/tables/CassandraSieveTable.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.sieve.cassandra.tables;
+
+public interface CassandraSieveTable {
+ String TABLE_NAME = "sieve";
+
+ String USER_NAME = "user_name";
+ String SCRIPT_NAME = "script_name";
+ String SCRIPT_CONTENT = "script_content";
+ String IS_ACTIVE = "is_active";
+ String DATE = "date";
+ String SIZE = "size";
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryTest.java b/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryTest.java
new file mode 100644
index 0000000..14ff4e8
--- /dev/null
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/CassandraSieveRepositoryTest.java
@@ -0,0 +1,82 @@
+/****************************************************************
+ * 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.sieve.cassandra;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Date;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.sieve.cassandra.tables.CassandraSieveTable;
+import org.apache.james.sieverepository.api.SieveRepository;
+import org.apache.james.sieverepository.api.exception.ScriptNotFoundException;
+import org.apache.james.sieverepository.lib.AbstractSieveRepositoryTest;
+import org.joda.time.DateTime;
+import org.junit.Test;
+
+public class CassandraSieveRepositoryTest extends AbstractSieveRepositoryTest {
+ public static final int DATE_TIMESTAMP = 123456141;
+ private CassandraCluster cassandra;
+
+ public CassandraSieveRepositoryTest() {
+ cassandra = CassandraCluster.create(new CassandraSieveRepositoryModule());
+ }
+
+ @Override
+ protected SieveRepository createSieveRepository() throws Exception {
+ return new CassandraSieveRepository(new CassandraSieveDAO(cassandra.getConf()));
+ }
+
+ @Override
+ protected void cleanUp() throws Exception {
+ cassandra.clearAllTables();
+ }
+
+ @Test
+ public void getActivationDateForActiveScriptShouldWork() throws Exception {
+ cassandra.getConf().execute(
+ insertInto(CassandraSieveTable.TABLE_NAME)
+ .value(CassandraSieveTable.USER_NAME, USER)
+ .value(CassandraSieveTable.SCRIPT_NAME, SCRIPT_NAME)
+ .value(CassandraSieveTable.SCRIPT_CONTENT, SCRIPT_CONTENT)
+ .value(CassandraSieveTable.IS_ACTIVE, true)
+ .value(CassandraSieveTable.SIZE, SCRIPT_CONTENT.length())
+ .value(CassandraSieveTable.DATE, new Date(DATE_TIMESTAMP))
+ );
+ assertThat(sieveRepository.getActivationDateForActiveScript(USER)).isEqualTo(new DateTime(DATE_TIMESTAMP));
+ }
+
+
+ @Test(expected = ScriptNotFoundException.class)
+ public void getActivationDateForActiveScriptShouldThrowOnMissingActiveScript() throws Exception {
+ cassandra.getConf().execute(
+ insertInto(CassandraSieveTable.TABLE_NAME)
+ .value(CassandraSieveTable.USER_NAME, USER)
+ .value(CassandraSieveTable.SCRIPT_NAME, SCRIPT_NAME)
+ .value(CassandraSieveTable.SCRIPT_CONTENT, SCRIPT_CONTENT)
+ .value(CassandraSieveTable.IS_ACTIVE, false)
+ .value(CassandraSieveTable.SIZE, SCRIPT_CONTENT.length())
+ .value(CassandraSieveTable.DATE, DATE_TIMESTAMP)
+ );
+ sieveRepository.getActivationDateForActiveScript(USER);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/model/SieveQuotaTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/model/SieveQuotaTest.java b/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/model/SieveQuotaTest.java
new file mode 100644
index 0000000..fe93a82
--- /dev/null
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/sieve/cassandra/model/SieveQuotaTest.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * 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.sieve.cassandra.model;
+
+import java.util.Optional;
+
+import org.apache.james.sieverepository.api.exception.QuotaExceededException;
+import org.junit.Test;
+
+public class SieveQuotaTest {
+
+ public static final long INVALID_VALUE = -1L;
+ public static final long LIMIT_LOW_VALUE = 10L;
+ public static final long SIZE_DIFFERENCE = 20L;
+ public static final int CURRENT_USAGE = 0;
+ public static final long LIMIT_HIGH_VALUE = 100L;
+
+ @Test(expected = IllegalArgumentException.class)
+ public void sieveQuotaShouldThrowOnNegativeCurrentValue() {
+ new SieveQuota(INVALID_VALUE, Optional.empty());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void sieveQuotaShouldThrowOnNegativeLimitValue() {
+ new SieveQuota(0, Optional.of(INVALID_VALUE));
+ }
+
+ @Test(expected = QuotaExceededException.class)
+ public void checkOverQuotaUponModificationShouldThrowIfLimitExceeded() throws Exception {
+ new SieveQuota(CURRENT_USAGE, Optional.of(LIMIT_LOW_VALUE)).checkOverQuotaUponModification(SIZE_DIFFERENCE);
+ }
+
+ @Test
+ public void checkOverQuotaShouldNotThrowWhenNoLimit() throws Exception {
+ new SieveQuota(CURRENT_USAGE, Optional.empty()).checkOverQuotaUponModification(SIZE_DIFFERENCE);
+ }
+
+ @Test
+ public void checkOverQuotaUponModificationShouldNotThrowIfLimitNotExceeded() throws Exception {
+ new SieveQuota(CURRENT_USAGE, Optional.of(LIMIT_HIGH_VALUE)).checkOverQuotaUponModification(SIZE_DIFFERENCE);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/96dfc5d5/server/data/data-library/src/test/java/org/apache/james/sieverepository/lib/AbstractSieveRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-library/src/test/java/org/apache/james/sieverepository/lib/AbstractSieveRepositoryTest.java b/server/data/data-library/src/test/java/org/apache/james/sieverepository/lib/AbstractSieveRepositoryTest.java
index 1488791..b07d3dc 100644
--- a/server/data/data-library/src/test/java/org/apache/james/sieverepository/lib/AbstractSieveRepositoryTest.java
+++ b/server/data/data-library/src/test/java/org/apache/james/sieverepository/lib/AbstractSieveRepositoryTest.java
@@ -31,22 +31,24 @@ import org.apache.james.sieverepository.api.exception.IsActiveException;
import org.apache.james.sieverepository.api.exception.QuotaExceededException;
import org.apache.james.sieverepository.api.exception.QuotaNotFoundException;
import org.apache.james.sieverepository.api.exception.ScriptNotFoundException;
+import org.joda.time.DateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public abstract class AbstractSieveRepositoryTest {
- private static final String USER = "test";
- private static final String SCRIPT_NAME = "script";
- private static final String SCRIPT_CONTENT = "\u0048\u0065\u006C\u006C\u006F World"; // test utf-8
+ protected static final String USER = "test";
+ protected static final String SCRIPT_NAME = "script";
+ protected static final String SCRIPT_CONTENT = "\u0048\u0065\u006C\u006C\u006F World"; // test utf-8
+
private static final String OTHER_SCRIPT_NAME = "other_script";
private static final String OTHER_SCRIPT_CONTENT = "Other script content";
private static final long DEFAULT_QUOTA = Long.MAX_VALUE - 1L;
private static final long USER_QUOTA = Long.MAX_VALUE / 2;
private static final String UTF8_ENCODING = "UTF-8";
- private SieveRepository sieveRepository;
+ protected SieveRepository sieveRepository;
@Before
public void setUp() throws Exception {
@@ -70,6 +72,20 @@ public abstract class AbstractSieveRepositoryTest {
}
@Test
+ public void getActivationDateForActiveScriptShouldReturnNonNullAndNonZeroResult() throws Exception {
+ sieveRepository.putScript(USER, SCRIPT_NAME, SCRIPT_CONTENT);
+ sieveRepository.setActive(USER, SCRIPT_NAME);
+ assertThat(sieveRepository.getActivationDateForActiveScript(USER)).isNotNull();
+ assertThat(sieveRepository.getActivationDateForActiveScript(USER)).isNotEqualTo(new DateTime(0L));
+ }
+
+ @Test(expected = ScriptNotFoundException.class)
+ public void getActivationDateForActiveScriptShouldThrowOnMissingActiveScript() throws Exception {
+ sieveRepository.putScript(USER, SCRIPT_NAME, SCRIPT_CONTENT);
+ sieveRepository.getActivationDateForActiveScript(USER);
+ }
+
+ @Test
public void haveSpaceShouldNotThrowWhenUserDoesNotHaveQuota() throws Exception {
sieveRepository.haveSpace(USER, SCRIPT_NAME, DEFAULT_QUOTA + 1L);
}
@@ -185,7 +201,7 @@ public abstract class AbstractSieveRepositoryTest {
public void switchOffActiveScriptShouldWork() throws Exception {
sieveRepository.putScript(USER, SCRIPT_NAME, SCRIPT_CONTENT);
sieveRepository.setActive(USER, SCRIPT_NAME);
- sieveRepository.setActive(USER, "");
+ sieveRepository.setActive(USER, SieveRepository.NO_SCRIPT_NAME);
sieveRepository.getActive(USER);
}
@@ -193,7 +209,7 @@ public abstract class AbstractSieveRepositoryTest {
public void switchOffActiveScriptShouldNotThrow() throws Exception {
sieveRepository.putScript(USER, SCRIPT_NAME, SCRIPT_CONTENT);
sieveRepository.setActive(USER, SCRIPT_NAME);
- sieveRepository.setActive(USER, "");
+ sieveRepository.setActive(USER, SieveRepository.NO_SCRIPT_NAME);
}
@Test(expected = ScriptNotFoundException.class)
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org