All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.activej.fs.MountingActiveFs Maven / Gradle / Ivy

Go to download

Provides tools for building efficient, scalable local, remote or clustered file servers. It utilizes ActiveJ CSP for fast and reliable file transfer.

There is a newer version: 6.0-beta2
Show newest version
/*
 * Copyright (C) 2020 ActiveJ LLC.
 *
 * Licensed 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 io.activej.fs;

import io.activej.async.function.AsyncBiConsumer;
import io.activej.async.function.AsyncConsumer;
import io.activej.async.function.AsyncSupplier;
import io.activej.bytebuf.ByteBuf;
import io.activej.common.collection.Try;
import io.activej.common.tuple.Tuple2;
import io.activej.csp.ChannelConsumer;
import io.activej.csp.ChannelSupplier;
import io.activej.fs.exception.FsBatchException;
import io.activej.fs.exception.FsScalarException;
import io.activej.promise.Promise;
import io.activej.promise.Promises;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Stream;

import static io.activej.common.Checks.checkArgument;
import static io.activej.common.Utils.isBijection;
import static java.util.stream.Collectors.*;

/**
 * A file system that allows to mount several {@link ActiveFs} implementations to correspond to different filenames.
 * 

* Inherits the most strict limitations of all the mounted file systems implementations and root file system. */ final class MountingActiveFs implements ActiveFs { private final ActiveFs root; private final Map mounts; MountingActiveFs(ActiveFs root, Map mounts) { this.root = root; this.mounts = mounts; } private ActiveFs findMount(String filename) { int idx = filename.lastIndexOf('/'); while (idx != -1) { String path = filename.substring(0, idx); ActiveFs mount = mounts.get(path); if (mount != null) { return mount; } idx = filename.lastIndexOf('/', idx - 1); } return root; } @Override public Promise> upload(@NotNull String name) { return findMount(name).upload(name); } @Override public Promise> upload(@NotNull String name, long size) { return findMount(name).upload(name, size); } @Override public Promise> append(@NotNull String name, long offset) { return findMount(name).append(name, offset); } @Override public Promise> download(@NotNull String name, long offset, long limit) { return findMount(name).download(name, offset, limit); } @Override public Promise> list(@NotNull String glob) { return Promises.toList(Stream.concat(Stream.of(root), mounts.values().stream()).map(f -> f.list(glob))) .map(listOfMaps -> FileMetadata.flatten(listOfMaps.stream())); } @Override public Promise<@Nullable FileMetadata> info(@NotNull String name) { return findMount(name).info(name); } @Override public Promise> infoAll(@NotNull Set names) { Map result = new HashMap<>(); return Promises.all(names.stream() .collect(groupingBy(this::findMount, toSet())) .entrySet().stream() .map(entry -> entry.getKey() .infoAll(entry.getValue()) .whenResult(result::putAll))) .map($ -> result); } @Override public Promise copy(@NotNull String name, @NotNull String target) { return transfer(name, target, (s, t) -> fs -> fs.copy(s, t), false); } @Override public Promise copyAll(Map sourceToTarget) { checkArgument(isBijection(sourceToTarget), "Targets must be unique"); if (sourceToTarget.isEmpty()) return Promise.complete(); return transfer(sourceToTarget, ActiveFs::copyAll, false); } @Override public Promise move(@NotNull String name, @NotNull String target) { return transfer(name, target, (s, t) -> fs -> fs.move(s, t), true); } @Override public Promise moveAll(Map sourceToTarget) { checkArgument(isBijection(sourceToTarget), "Targets must be unique"); if (sourceToTarget.isEmpty()) return Promise.complete(); return transfer(sourceToTarget, ActiveFs::moveAll, true); } @Override public Promise delete(@NotNull String name) { return findMount(name).delete(name); } @Override public Promise deleteAll(Set toDelete) { return Promises.all(toDelete.stream() .collect(groupingBy(this::findMount, IdentityHashMap::new, toSet())) .entrySet().stream() .map(entry -> entry.getKey().deleteAll(entry.getValue()))); } private Promise transfer(String source, String target, BiFunction> action, boolean deleteSource) { ActiveFs first = findMount(source); ActiveFs second = findMount(target); if (first == second) { return action.apply(source, target).accept(first); } return first.download(source) .then(supplier -> supplier.streamTo(second.upload(target))) .then(() -> deleteSource ? first.delete(source) : Promise.complete()); } private Promise transfer(Map sourceToTarget, AsyncBiConsumer> action, boolean deleteSource) { List>>> movePromises = new ArrayList<>(); Map> groupedBySameFs = new IdentityHashMap<>(); for (Map.Entry entry : sourceToTarget.entrySet()) { String source = entry.getKey(); String target = entry.getValue(); ActiveFs first = findMount(source); ActiveFs second = findMount(target); if (first == second) { groupedBySameFs .computeIfAbsent(first, $ -> new HashMap<>()) .put(source, target); } else { movePromises.add(() -> first.download(source) .then(supplier -> supplier.streamTo(second.upload(target))) .then(() -> deleteSource ? first.delete(target) : Promise.complete()) .toTry() .map(aTry -> new Tuple2<>(source, aTry))); } } for (Map.Entry> entry : groupedBySameFs.entrySet()) { movePromises.add(() -> action.accept(entry.getKey(), entry.getValue()).toTry().map(aTry -> new Tuple2<>("", aTry))); } return Promises.toList(movePromises.stream().map(AsyncSupplier::get)) .whenResult(list -> { List> exceptions = list.stream() .filter(tuple -> tuple.getValue2().isException()) .map(tuple -> new Tuple2<>(tuple.getValue1(), tuple.getValue2().getException())) .collect(toList()); if (!exceptions.isEmpty()) { Map scalarExceptions = new HashMap<>(); for (Tuple2 tuple : exceptions) { Exception exception = tuple.getValue2(); if (exception instanceof FsScalarException) { scalarExceptions.put(tuple.getValue1(), (FsScalarException) exception); } else if (exception instanceof FsBatchException) { scalarExceptions.putAll(((FsBatchException) exception).getExceptions()); } else { throw exception; } } throw new FsBatchException(scalarExceptions); } }) .toVoid(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy