com.github.horrorho.inflatabledonkey.cloud.AssetPool.java Source code

Java tutorial

Introduction

Here is the source code for com.github.horrorho.inflatabledonkey.cloud.AssetPool.java

Source

/*
 * The MIT License
 *
 * Copyright 2016 Ahseya.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.github.horrorho.inflatabledonkey.cloud;

import com.github.horrorho.inflatabledonkey.data.backup.Asset;
import com.github.horrorho.inflatabledonkey.protobuf.ChunkServer.ChunkInfo;
import com.github.horrorho.inflatabledonkey.protobuf.ChunkServer.StorageHostChunkList;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
import org.apache.http.client.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Ahseya
 */
@ThreadSafe
public class AssetPool {

    static Set<Asset> validate(Set<Asset> assets) {
        return assets.stream().filter(u -> {
            if (u.fileSignature().isPresent()) {
                return true;
            }
            logger.warn("-- validate() - missing file signature: {}", u);
            return false;
        }).collect(toSet());
    }

    private static final Logger logger = LoggerFactory.getLogger(AssetPool.class);

    private final Object lock;
    private final AuthorizeAssetsClient authorizeAssets;
    private final VoodooChunkEncryptionKeyMapper keyMapper;
    private Set<Asset> assets;
    private ItemElements<Asset, ByteString> assetChunks = null;

    public AssetPool(Object lock, AuthorizeAssetsClient authorizeAssets, VoodooChunkEncryptionKeyMapper keyMapper,
            Set<Asset> assets) {
        this.lock = Objects.requireNonNull(lock);
        this.authorizeAssets = Objects.requireNonNull(authorizeAssets);
        this.keyMapper = Objects.requireNonNull(keyMapper);
        this.assets = validate(assets);
    }

    public AssetPool(AuthorizeAssetsClient authorizeAssets, VoodooChunkEncryptionKeyMapper keyMapper,
            Set<Asset> assets) {
        this(new Object(), authorizeAssets, keyMapper, assets);
    }

    public AssetPool(Set<Asset> assets) {
        this(AuthorizeAssetsClient.backupd(), VoodooChunkEncryptionKeyMapper.defaultInstance(), assets);
    }

    public Collection<StorageHostChunkList> authorize(HttpClient httpClient) throws IOException {
        synchronized (lock) {
            logger.trace("<< authorize()");
            Set<Asset> a = assets == null ? assetChunks.items() : assets;
            Map<ByteString, Asset> fileSignatureToAsset = a.stream()
                    .collect(toMap(u -> ByteString.copyFrom(u.fileSignature().get()), Function.identity()));
            Collection<StorageHostChunkList> containers = authorize(httpClient, fileSignatureToAsset);
            logger.trace(">> authorize() - containers: {}", containers.size());
            return containers;
        }
    }

    @GuardedBy("lock")
    Collection<StorageHostChunkList> authorize(HttpClient httpClient, Map<ByteString, Asset> fileSignatureToAsset)
            throws IOException {

        List<Voodoo> voodooList = voodoos(httpClient, fileSignatureToAsset);
        assetChunks = assetChunks(voodooList, fileSignatureToAsset);
        assets = null;
        return voodooList.stream().map(Voodoo::containers).collect(ArrayList::new, List::addAll, List::addAll);
    }

    List<Voodoo> voodoos(HttpClient httpClient, Map<ByteString, Asset> fileSignatureToAsset) throws IOException {
        Function<ByteString, Optional<byte[]>> keyEncryptionKey = u -> Optional
                .ofNullable(fileSignatureToAsset.get(u)).flatMap(Asset::keyEncryptionKey);

        return authorizeAssets.apply(httpClient, fileSignatureToAsset.values()).stream().map(VoodooFactory::from)
                .flatMap(Collection::stream).map(u -> keyMapper.apply(u, keyEncryptionKey)).collect(toList());
    }

    ItemElements<Asset, ByteString> assetChunks(List<Voodoo> voodoos, Map<ByteString, Asset> fileSignatureToAsset) {
        Map<Asset, List<ByteString>> map = voodoos.stream().map(Voodoo::fileSignatureToChunkChecksumList)
                .map(Map::entrySet).flatMap(Collection::stream).filter(e -> {
                    if (fileSignatureToAsset.containsKey(e.getKey())) {
                        return true;
                    }
                    logger.warn("-- assetChunks() - unreferenced signature: {}", e.getKey());
                    return false;
                }).map(e -> new SimpleImmutableEntry<>(fileSignatureToAsset.get(e.getKey()), e.getValue()))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (u, v) -> {
                    if (!u.equals(v)) {
                        logger.warn("-- assetChunks() - collision: {} {}", u, v);
                    }
                    return u;
                }));
        return new ItemElements<>(map);
    }

    public Map<Asset, Optional<List<ByteString>>> put(StorageHostChunkList container,
            Collection<ByteString> chunkChecksums) {
        synchronized (lock) {
            if (assetChunks == null) {
                throw new IllegalStateException("not authorized");
            }
            Set<ByteString> missing = container.getChunkInfoList().stream().map(ChunkInfo::getChunkChecksum)
                    .collect(toSet());
            missing.removeAll(chunkChecksums);
            logger.debug("-- debug() - missing: {}", missing);

            Map<Asset, Optional<List<ByteString>>> map = putElements(chunkChecksums);
            map.putAll(voidElements(missing));
            return map;
        }
    }

    @GuardedBy("lock")
    Map<Asset, Optional<List<ByteString>>> putElements(Collection<ByteString> chunkChecksums) {
        logger.trace("<< putElements() - chunk checksums: {}", chunkChecksums);
        Map<Asset, Optional<List<ByteString>>> map = assetChunks.putElements(chunkChecksums).entrySet().stream()
                .map(e -> new SimpleImmutableEntry<>(e.getKey(), Optional.of(e.getValue())))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
        logger.trace(">> putElements() - assets: {}", map.size());
        return map;
    }

    @GuardedBy("lock")
    Map<Asset, Optional<List<ByteString>>> voidElements(Collection<ByteString> chunkChecksums) {
        logger.trace("<< voidElements() - chunk checksums: {}", chunkChecksums);
        Map<Asset, Optional<List<ByteString>>> map = assetChunks.voidElements(chunkChecksums).stream()
                .collect(toMap(Function.identity(), u -> Optional.<List<ByteString>>empty()));
        logger.trace(">> voidElements() - assets: {}", map.size());
        return map;
    }

    public boolean isEmpty() {
        synchronized (lock) {
            return assets == null ? assetChunks.isEmpty() : assets.isEmpty();
        }
    }
}