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

org.gradle.internal.vfs.impl.DefaultFileSystemAccess Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019 the original author or authors.
 *
 * 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 org.gradle.internal.vfs.impl;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.common.util.concurrent.Striped;
import org.gradle.internal.file.FileMetadata;
import org.gradle.internal.file.FileMetadata.AccessType;
import org.gradle.internal.file.FileType;
import org.gradle.internal.file.Stat;
import org.gradle.internal.hash.FileHasher;
import org.gradle.internal.hash.HashCode;
import org.gradle.internal.snapshot.FileSystemLocationSnapshot;
import org.gradle.internal.snapshot.FileSystemSnapshot;
import org.gradle.internal.snapshot.MissingFileSnapshot;
import org.gradle.internal.snapshot.RegularFileSnapshot;
import org.gradle.internal.snapshot.SnapshottingFilter;
import org.gradle.internal.snapshot.impl.DirectorySnapshotter;
import org.gradle.internal.snapshot.impl.DirectorySnapshotterStatistics;
import org.gradle.internal.snapshot.impl.FileSystemSnapshotFilter;
import org.gradle.internal.vfs.FileSystemAccess;
import org.gradle.internal.vfs.VirtualFileSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class DefaultFileSystemAccess implements FileSystemAccess {
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultFileSystemAccess.class);

    private final VirtualFileSystem virtualFileSystem;
    private final Stat stat;
    private final Interner stringInterner;
    private final WriteListener writeListener;
    private final DirectorySnapshotterStatistics.Collector statisticsCollector;
    private ImmutableList defaultExcludes;
    private DirectorySnapshotter directorySnapshotter;
    private final FileHasher hasher;
    private final StripedProducerGuard producingSnapshots = new StripedProducerGuard<>();

    public DefaultFileSystemAccess(
        FileHasher hasher,
        Interner stringInterner,
        Stat stat,
        VirtualFileSystem virtualFileSystem,
        WriteListener writeListener,
        DirectorySnapshotterStatistics.Collector statisticsCollector,
        String... defaultExcludes
    ) {
        this.stringInterner = stringInterner;
        this.stat = stat;
        this.writeListener = writeListener;
        this.statisticsCollector = statisticsCollector;
        this.defaultExcludes = ImmutableList.copyOf(defaultExcludes);
        this.directorySnapshotter = new DirectorySnapshotter(hasher, stringInterner, this.defaultExcludes, statisticsCollector);
        this.hasher = hasher;
        this.virtualFileSystem = virtualFileSystem;
    }

    @Override
    public  T read(String location, Function visitor) {
        return visitor.apply(readLocation(location));
    }

    @Override
    public  Optional readRegularFileContentHash(String location, Function visitor) {
        return virtualFileSystem.getMetadata(location)
            .>flatMap(snapshot -> {
                if (snapshot.getType() != FileType.RegularFile) {
                    return Optional.of(Optional.empty());
                }
                if (snapshot instanceof FileSystemLocationSnapshot) {
                    return Optional.of(Optional.of(((FileSystemLocationSnapshot) snapshot).getHash()));
                }
                return Optional.empty();
            })
            .orElseGet(() -> {
                File file = new File(location);
                FileMetadata fileMetadata = this.stat.stat(file);
                if (fileMetadata.getType() == FileType.Missing) {
                    storeMetadataForMissingFile(location, fileMetadata.getAccessType());
                }
                if (fileMetadata.getType() != FileType.RegularFile) {
                    return Optional.empty();
                }
                HashCode hash = producingSnapshots.guardByKey(location,
                    () -> virtualFileSystem.getSnapshot(location)
                        .orElseGet(() -> {
                            HashCode hashCode = hasher.hash(file, fileMetadata.getLength(), fileMetadata.getLastModified());
                            RegularFileSnapshot snapshot = new RegularFileSnapshot(location, file.getName(), hashCode, fileMetadata);
                            virtualFileSystem.store(snapshot.getAbsolutePath(), snapshot);
                            return snapshot;
                        }).getHash());
                return Optional.of(hash);
            })
            .map(visitor);
    }

    private void storeMetadataForMissingFile(String location, AccessType accessType) {
        virtualFileSystem.store(location, new MissingFileSnapshot(location, accessType));
    }

    @Override
    public void read(String location, SnapshottingFilter filter, Consumer visitor) {
        if (filter.isEmpty()) {
            visitor.accept(readLocation(location));
        } else {
            FileSystemSnapshot filteredSnapshot = readSnapshotFromLocation(location,
                snapshot -> FileSystemSnapshotFilter.filterSnapshot(filter.getAsSnapshotPredicate(), snapshot),
                () -> {
                    FileSystemLocationSnapshot snapshot = snapshot(location, filter);
                    return snapshot.getType() == FileType.Directory
                        // Directory snapshots have been filtered while walking the file system
                        ? snapshot
                        : FileSystemSnapshotFilter.filterSnapshot(filter.getAsSnapshotPredicate(), snapshot);
                });

            if (filteredSnapshot instanceof FileSystemLocationSnapshot) {
                visitor.accept((FileSystemLocationSnapshot) filteredSnapshot);
            }
        }
    }

    private FileSystemLocationSnapshot snapshot(String location, SnapshottingFilter filter) {
        File file = new File(location);
        FileMetadata fileMetadata = this.stat.stat(file);
        switch (fileMetadata.getType()) {
            case RegularFile:
                HashCode hash = hasher.hash(file, fileMetadata.getLength(), fileMetadata.getLastModified());
                RegularFileSnapshot regularFileSnapshot = new RegularFileSnapshot(location, file.getName(), hash, fileMetadata);
                virtualFileSystem.store(regularFileSnapshot.getAbsolutePath(), regularFileSnapshot);
                return regularFileSnapshot;
            case Missing:
                MissingFileSnapshot missingFileSnapshot = new MissingFileSnapshot(location, fileMetadata.getAccessType());
                virtualFileSystem.store(missingFileSnapshot.getAbsolutePath(), missingFileSnapshot);
                return missingFileSnapshot;
            case Directory:
                AtomicBoolean hasBeenFiltered = new AtomicBoolean(false);
                FileSystemLocationSnapshot directorySnapshot = directorySnapshotter.snapshot(location, filter.isEmpty() ? null : filter.getAsDirectoryWalkerPredicate(), hasBeenFiltered);
                if (!hasBeenFiltered.get()) {
                    virtualFileSystem.store(directorySnapshot.getAbsolutePath(), directorySnapshot);
                }
                return directorySnapshot;
            default:
                throw new UnsupportedOperationException();
        }
    }

    private FileSystemLocationSnapshot readLocation(String location) {
        return readSnapshotFromLocation(location, () -> snapshot(location, SnapshottingFilter.EMPTY));
    }

    private FileSystemLocationSnapshot readSnapshotFromLocation(
        String location,
        Supplier readFromDisk
    ) {
        return readSnapshotFromLocation(
            location,
            Function.identity(),
            readFromDisk
        );
    }

    private  T readSnapshotFromLocation(
        String location,
        Function snapshotProcessor,
        Supplier readFromDisk
    ) {
        return virtualFileSystem.getSnapshot(location)
            .map(snapshotProcessor)
            // Avoid snapshotting the same location at the same time
            .orElseGet(() -> producingSnapshots.guardByKey(location,
                () -> virtualFileSystem.getSnapshot(location)
                    .map(snapshotProcessor)
                    .orElseGet(readFromDisk)
            ));
    }

    @Override
    public void write(Iterable locations, Runnable action) {
        writeListener.locationsWritten(locations);
        virtualFileSystem.invalidate(locations);
        action.run();
    }

    @Override
    public void record(FileSystemLocationSnapshot snapshot) {
        virtualFileSystem.store(snapshot.getAbsolutePath(), snapshot);
    }

    private static class StripedProducerGuard {
        private final Striped locks = Striped.lock(Runtime.getRuntime().availableProcessors() * 4);

        public  V guardByKey(T key, Supplier supplier) {
            Lock lock = locks.get(key);
            try {
                lock.lock();
                return supplier.get();
            } finally {
                lock.unlock();
            }
        }
    }

    public void updateDefaultExcludes(String... newDefaultExcludesArgs) {
        ImmutableList newDefaultExcludes = ImmutableList.copyOf(newDefaultExcludesArgs);
        if (!defaultExcludes.equals(newDefaultExcludes)) {
            LOGGER.debug("Default excludes changes from {} to {}", defaultExcludes, newDefaultExcludes);
            defaultExcludes = newDefaultExcludes;
            directorySnapshotter = new DirectorySnapshotter(hasher, stringInterner, newDefaultExcludes, statisticsCollector);
            virtualFileSystem.invalidateAll();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy