org.talend.dataprep.folder.store.file.FileSystemFolderRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.talend.dataprep.folder.store.file.FileSystemFolderRepository.java

Source

// ============================================================================
// Copyright (C) 2006-2016 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// https://github.com/Talend/data-prep/blob/master/LICENSE
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================

package org.talend.dataprep.folder.store.file;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.talend.daikon.exception.ExceptionContext.build;
import static org.talend.dataprep.api.folder.FolderBuilder.folder;
import static org.talend.dataprep.exception.error.DataSetErrorCodes.*;
import static org.talend.dataprep.exception.error.FolderErrorCodes.FOLDER_NOT_EMPTY;
import static org.talend.dataprep.folder.store.FoldersRepositoriesConstants.PATH_SEPARATOR;
import static org.talend.dataprep.folder.store.file.FileSystemUtils.*;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.PostConstruct;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.talend.dataprep.api.folder.Folder;
import org.talend.dataprep.api.folder.FolderContentType;
import org.talend.dataprep.api.folder.FolderEntry;
import org.talend.dataprep.api.share.Owner;
import org.talend.dataprep.exception.TDPException;
import org.talend.dataprep.exception.error.DataSetErrorCodes;
import org.talend.dataprep.folder.store.FolderRepository;
import org.talend.dataprep.security.Security;
import org.talend.dataprep.util.StringsHelper;

/**
 * File system folder repository implementation.
 */
@Component("folderRepository#file")
@ConditionalOnProperty(name = "folder.store", havingValue = "file")
public class FileSystemFolderRepository implements FolderRepository {

    @Autowired
    private Security security;

    @Autowired
    private PathsConverter pathsConverter;

    /**
     * Make sure the root folder is there.
     */
    @PostConstruct
    private void init() {
        try {
            Path rootPath = pathsConverter.getRootFolder();
            if (!Files.exists(rootPath)) {
                Files.createDirectories(rootPath);
            }
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    @Override
    public Folder getHome() {
        return toFolder(pathsConverter.getRootFolder(), security.getUserId());
    }

    @Override
    public boolean exists(String folderId) {
        FolderPath path = fromId(folderId);
        return path != null && Files.exists(pathsConverter.toPath(path));
    }

    @Override
    public Iterable<Folder> children(String parentId) {
        final FolderPath parentDpPath = fromId(parentId);
        try {
            Path folderPath;
            if (parentDpPath != null) {
                folderPath = pathsConverter.toPath(parentDpPath);
            } else {
                folderPath = pathsConverter.getRootFolder();
            }
            List<Folder> children;
            if (Files.notExists(folderPath)) {
                children = emptyList();
            } else {
                try (Stream<Path> childrenStream = Files.list(folderPath)) {
                    children = childrenStream //
                            .map(p -> toFolderIfDirectory(p, security.getUserId())) //
                            .filter(Objects::nonNull) //
                            .collect(toList());
                }
            }
            return children;
        } catch (IOException e) {
            throw new TDPException(UNABLE_TO_LIST_FOLDER_CHILDREN, e,
                    build().put("path", parentDpPath == null ? null : parentDpPath.serializeAsString()));
        }
    }

    @Override
    public Folder addFolder(String parentId, String givenPath) {
        // parent path must be set and exists
        FolderPath parentFolderPath = fromId(parentId);

        List<String> pathToAppend = Arrays.stream(givenPath.split(PATH_SEPARATOR.toString()))
                .filter(StringUtils::isNotBlank).collect(Collectors.toList());

        if (parentFolderPath == null || !exists(parentId)) {
            throw new TDPException(UNABLE_TO_ADD_FOLDER, build().put("path", givenPath));
        }

        FolderPath folderPathToCreate = new FolderPath(parentFolderPath,
                pathToAppend.toArray(new String[pathToAppend.size()]));
        try {
            Path pathToCreate = pathsConverter.toPath(folderPathToCreate);
            Files.createDirectories(pathToCreate);
            return toFolder(pathToCreate, security.getUserId());
        } catch (IOException e) {
            throw new TDPException(UNABLE_TO_ADD_FOLDER, e, build().put("path", givenPath));
        }
    }

    @Override
    public Folder getFolderById(String folderId) {
        final FolderPath folderDpPath = fromId(folderId);
        final Path folderPath = pathsConverter.toPath(folderDpPath);
        return toFolder(folderPath, security.getUserId());
    }

    @Override
    public Folder renameFolder(String folderId, String newName) {

        final Folder folder = getFolderById(folderId);

        if (folder == null) {
            throw new IllegalArgumentException("Cannot rename a folder that cannot be found");
        }

        FolderPath folderToMovePath = FolderPath.deserializeFromString(folder.getPath());

        if (folderToMovePath.isRoot()) {
            throw new IllegalArgumentException("Cannot rename home folder");
        }

        if (newName.contains(PATH_SEPARATOR.toString())) {
            throw new TDPException(ILLEGAL_FOLDER_NAME, build().put("name", newName));
        }

        FolderPath targetFolderPath = new FolderPath(folderToMovePath.getParent(), newName);
        Path folderPath = pathsConverter.toPath(folderToMovePath);
        Path newFolderPath = pathsConverter.toPath(targetFolderPath);

        try {
            FileUtils.moveDirectory(folderPath.toFile(), newFolderPath.toFile());
        } catch (IOException e) {
            throw new TDPException(UNABLE_TO_RENAME_FOLDER, e, build().put("path", folder.getPath()));
        }

        return getFolderById(toId(pathsConverter.toFolderPath(newFolderPath)));
    }

    @Override
    public FolderEntry addFolderEntry(FolderEntry folderEntry, String folderId) {
        FolderPath folderPath = fromId(folderId);

        if (folderPath == null) {
            throw new TDPException(FOLDER_DOES_NOT_EXIST, build().put("id", folderId));
        }

        // we store the FolderEntry bean content as properties the file name is the name
        try {
            String fileName = buildFileName(folderEntry);

            Path entryFilepath = pathsConverter.toPath(folderPath).resolve(fileName);

            // we delete it if exists
            Files.deleteIfExists(entryFilepath);

            Path parentPath = entryFilepath.getParent();
            // check parent path first
            if (Files.notExists(parentPath)) {
                Files.createDirectories(parentPath);
            }

            entryFilepath = Files.createFile(entryFilepath);
            folderEntry.setFolderId(folderId);

            try (OutputStream outputStream = Files.newOutputStream(entryFilepath)) {
                writeEntryToStream(folderEntry, outputStream);
            }
            return folderEntry;
        } catch (IOException e) {
            throw new TDPException(UNABLE_TO_ADD_FOLDER_ENTRY, e, build().put("path", folderPath));
        }
    }

    @Override
    public void removeFolderEntry(String folderId, String contentId, FolderContentType contentType) {

        if (contentType == null) {
            throw new IllegalArgumentException(
                    "The content type of the folder entry to be removed cannot be null.");
        }

        final FolderPath folderPath = fromId(folderId);

        try {
            Path path = pathsConverter.toPath(folderPath);

            try (Stream<Path> paths = Files.list(path)) {
                paths //
                        .filter(pathFound -> !Files.isDirectory(pathFound)) //
                        .filter(pathFile -> matches(pathFile, contentId, contentType)) //
                        .forEach(deleteFile());
            }
        } catch (IOException e) {
            throw new TDPException(UNABLE_TO_REMOVE_FOLDER_ENTRY, e, build().put("path", folderPath));
        }
    }

    @Override
    public void removeFolder(String folderId) {
        final Path path = pathsConverter.toPath(fromId(folderId));

        if (hasEntry(path)) {
            throw new TDPException(FOLDER_NOT_EMPTY);
        } else {
            try {
                FileUtils.deleteDirectory(path.toFile());
            } catch (IOException e) {
                throw new TDPException(UNABLE_TO_DELETE_FOLDER, e, build().put("path", path));
            }
        }
    }

    @Override
    public Iterable<FolderEntry> entries(String folderId, FolderContentType contentType) {
        FolderPath folderPath = fromId(folderId);

        if (folderPath == null) {
            throw new TDPException(FOLDER_DOES_NOT_EXIST, build().put("id", folderId));
        }

        final Path path = pathsConverter.toPath(folderPath);

        if (Files.notExists(path)) {
            return emptyList();
        }

        try {
            try (Stream<Path> paths = Files.list(path)) {
                return paths.filter(pathFound -> !Files.isDirectory(pathFound)) //
                        .map(FileSystemUtils::toFolderEntry) //
                        .filter(entry -> Objects.equals(contentType, entry.getContentType())) //
                        .collect(toList());
            }
        } catch (IOException e) {
            throw new TDPException(UNABLE_TO_LIST_FOLDER_ENTRIES, e,
                    build().put("path", path).put("type", contentType));
        }
    }

    @Override
    public Iterable<FolderEntry> findFolderEntries(String contentId, FolderContentType contentType) {
        try (Stream<Path> filesStream = Files.walk(pathsConverter.getRootFolder())) {
            return filesStream //
                    .filter(Files::isRegularFile) //
                    .map(FileSystemUtils::toFolderEntry) //
                    .filter(entry -> StringUtils.equals(entry.getContentId(), contentId) //
                            && Objects.equals(contentType, entry.getContentType())) //
                    .collect(toSet());
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public void clear() {
        try {
            FileUtils.deleteDirectory(pathsConverter.getRootFolder().toFile());
            init();
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public Iterable<Folder> searchFolders(String queryString, boolean strict) {
        try (Stream<Path> walk = Files.walk(pathsConverter.getRootFolder())) {
            return walk //
                    .filter(path -> //
                    Files.isDirectory(path) //
                            && StringsHelper.match(path.getFileName().toString(), queryString, strict)) //
                    .map(path -> toFolder(path, security.getUserId())) //
                    .collect(toList());
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public void copyFolderEntry(FolderEntry folderEntry, String destinationId) {
        FolderEntry cloned = new FolderEntry(folderEntry.getContentType(), folderEntry.getContentId());
        cloned.setFolderId(destinationId);
        addFolderEntry(cloned, destinationId);
    }

    @Override
    public void moveFolderEntry(FolderEntry folderEntry, String fromId, String toId) {
        Path destinationPath = pathsConverter.toPath(fromId(toId));
        if (Files.notExists(destinationPath)) {
            throw new IllegalArgumentException("destinationPath doesn't exists");
        }

        Path originFilePath = Paths.get(pathsConverter.toPath(fromId(fromId)).toString(),
                buildFileName(folderEntry));
        if (Files.notExists(originFilePath)) {
            throw new IllegalArgumentException("entry doesn't exists");
        }

        Path destinationFile = Paths.get(destinationPath.toString(), buildFileName(folderEntry));
        try {
            Files.move(originFilePath, destinationFile);
        } catch (IOException e) {
            throw new TDPException(DataSetErrorCodes.UNABLE_TO_MOVE_FOLDER_ENTRY, e);
        }
    }

    @Override
    public Folder locateEntry(String contentId, FolderContentType type) {
        try (Stream<Path> walk = Files.walk(pathsConverter.getRootFolder())) {
            return walk //
                    .filter(Files::isRegularFile) //
                    .filter(file -> {
                        FolderEntry folderEntry = FileSystemUtils.toFolderEntry(file);
                        return Objects.equals(folderEntry.getContentId(), contentId) //
                                && Objects.equals(folderEntry.getContentType(), type);
                    }) //
                    .findFirst() //
                    .map(Path::getParent) //
                    .map(p -> toFolder(p, security.getUserId())) //
                    .orElse(null);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public long size() {
        return countSubDirectories(pathsConverter.getRootFolder());
    }

    private static String buildFileName(FolderEntry folderEntry) {
        return folderEntry.getContentType().toString() + '@' + folderEntry.getContentId();
    }

    /** If the path represents a directory, build the {@link Folder} object based on it. */
    private Folder toFolderIfDirectory(Path path, String ownerId) {
        final Folder child;
        if (Files.isDirectory(path)) {
            child = toFolder(path, ownerId);
        } else {
            return null;
        }
        return child;
    }

    private Folder toFolder(Path filePath, String ownerId) {
        FolderPath folderPath = pathsConverter.toFolderPath(filePath);
        FolderInfo folderInfo = FolderInfo.create(filePath);
        if (folderInfo == null) {
            // TODO: we have a problem here: moving (or renaming) a file modify its parent, not itself at least on UNIX file system.
            long currentTime = System.currentTimeMillis();
            folderInfo = new FolderInfo(currentTime, currentTime);
        }
        return folder() //
                .path(folderPath.serializeAsString()) //
                .id(toId(folderPath)) //
                .parentId(folderPath.isRoot() ? null : toId(folderPath.getParent())) //
                .name(folderPath.getName()) //
                .ownerId(ownerId) //
                .owner(new Owner(ownerId, ownerId, "")) // default owner information
                .creationDate(folderInfo.getCreationDate()) //
                .lastModificationDate(folderInfo.getLastModificationDate()) //
                .build();
    }

}