org.apache.james.sieve.cassandra.CassandraSieveRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.james.sieve.cassandra.CassandraSieveRepository.java

Source

/****************************************************************
 * 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();
        }
    }

}