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

io.activej.fs.TransformActiveFs 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-rc2
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.bytebuf.ByteBuf;
import io.activej.common.function.FunctionEx;
import io.activej.csp.ChannelConsumer;
import io.activej.csp.ChannelSupplier;
import io.activej.fs.exception.ForbiddenPathException;
import io.activej.fs.exception.FsBatchException;
import io.activej.fs.exception.FsScalarException;
import io.activej.fs.util.RemoteFsUtils;
import io.activej.promise.Promise;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.AbstractMap.SimpleEntry;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

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

/**
 * A file system that can be configured to transform paths and filename via mapping functions.
 * 

* Inherits all the limitations of parent {@link ActiveFs} */ final class TransformActiveFs implements ActiveFs { private final ActiveFs parent; private final Function> into; private final Function> from; private final Function> globInto; TransformActiveFs(ActiveFs parent, Function> into, Function> from, Function> globInto) { this.parent = parent; this.into = into; this.from = from; this.globInto = globInto; } @Override public Promise> upload(@NotNull String name) { Optional transformed = into.apply(name); if (!transformed.isPresent()) { return Promise.ofException(new ForbiddenPathException()); } return parent.upload(transformed.get()); } @Override public Promise> upload(@NotNull String name, long size) { Optional transformed = into.apply(name); if (!transformed.isPresent()) { return Promise.ofException(new ForbiddenPathException()); } return parent.upload(transformed.get(), size); } @Override public Promise> append(@NotNull String name, long offset) { Optional transformed = into.apply(name); if (!transformed.isPresent()) { return Promise.ofException(new ForbiddenPathException()); } return parent.append(transformed.get(), offset); } @Override public Promise> download(@NotNull String name, long offset, long limit) { Optional transformed = into.apply(name); if (!transformed.isPresent()) { return Promise.ofException(new ForbiddenPathException()); } return parent.download(transformed.get(), offset, limit); } @Override public Promise copy(@NotNull String name, @NotNull String target) { return transfer(name, target, parent::copy); } @Override public Promise copyAll(Map sourceToTarget) { checkArgument(isBijection(sourceToTarget), "Targets must be unique"); if (sourceToTarget.isEmpty()) return Promise.complete(); return transfer(sourceToTarget, parent::copyAll); } @Override public Promise move(@NotNull String name, @NotNull String target) { return transfer(name, target, parent::move); } @Override public Promise moveAll(Map sourceToTarget) { checkArgument(isBijection(sourceToTarget), "Targets must be unique"); if (sourceToTarget.isEmpty()) return Promise.complete(); return transfer(sourceToTarget, parent::moveAll); } @Override public Promise> list(@NotNull String glob) { return globInto.apply(glob) .map(transformedGlob -> parent.list(transformedGlob) .map(transformMap($ -> true))) .orElseGet(() -> parent.list("**") .map(transformMap(RemoteFsUtils.getGlobStringPredicate(glob)))); } @Override public Promise<@Nullable FileMetadata> info(@NotNull String name) { return into.apply(name) .map(parent::info) .orElse(Promise.of(null)); } @Override public Promise> infoAll(@NotNull Set names) { Map result = new HashMap<>(); Set transformed = names.stream() .map(into) .filter(Optional::isPresent) .map(Optional::get) .collect(toSet()); return transformed.isEmpty() ? Promise.of(result) : parent.infoAll(transformed) .whenResult(map -> map.forEach((name, meta) -> { Optional maybeName = from.apply(name); assert maybeName.isPresent(); result.put(maybeName.get(), meta); })) .map($ -> result); } @Override public Promise ping() { return parent.ping(); } @Override public Promise delete(@NotNull String name) { Optional transformed = into.apply(name); if (!transformed.isPresent()) { return Promise.complete(); } return parent.delete(transformed.get()); } @Override public Promise deleteAll(Set toDelete) { return parent.deleteAll(toDelete.stream() .map(into) .filter(Optional::isPresent) .map(Optional::get) .collect(toSet())); } private Promise transfer(String source, String target, AsyncBiConsumer action) { Optional transformed = into.apply(source); if (!transformed.isPresent()) { return Promise.ofException(new ForbiddenPathException("Path '" + source + "' is forbidden")); } Optional transformedNew = into.apply(target); if (!transformedNew.isPresent()) { return Promise.ofException(new ForbiddenPathException("Path '" + target + "' is forbidden")); } return action.accept(transformed.get(), transformedNew.get()); } private Promise transfer(Map sourceToTarget, AsyncConsumer> action) { Map renamed = new LinkedHashMap<>(); Map exceptions = new HashMap<>(); for (Map.Entry entry : sourceToTarget.entrySet()) { String source = entry.getKey(); Optional transformed = into.apply(source); if (!transformed.isPresent()) { exceptions.put(source, new ForbiddenPathException("Path '" + source + "' is forbidden")); continue; } String target = entry.getValue(); Optional transformedNew = into.apply(target); if (!transformedNew.isPresent()) { exceptions.put(source, new ForbiddenPathException("Path '" + target + "' is forbidden")); continue; } renamed.put(transformed.get(), transformedNew.get()); } if (!exceptions.isEmpty()) { return Promise.ofException(new FsBatchException(exceptions)); } return action.accept(renamed); } private FunctionEx, Map> transformMap(Predicate postPredicate) { return map -> map.entrySet().stream() .map(entry -> from.apply(entry.getKey()) .map(mappedName -> new SimpleEntry<>(mappedName, entry.getValue()))) .filter(entry -> entry.isPresent() && postPredicate.test(entry.get().getKey())) .map(Optional::get) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy