fr.ortolang.diffusion.client.cmd.CheckBagCommand.java Source code

Java tutorial

Introduction

Here is the source code for fr.ortolang.diffusion.client.cmd.CheckBagCommand.java

Source

package fr.ortolang.diffusion.client.cmd;

/*
 * #%L
 * ORTOLANG
 * A online network structure for hosting language resources and tools.
 * 
 * Jean-Marie Pierrel / ATILF UMR 7118 - CNRS / Universit de Lorraine
 * Etienne Petitjean / ATILF UMR 7118 - CNRS
 * Jrme Blanchard / ATILF UMR 7118 - CNRS
 * Bertrand Gaiffe / ATILF UMR 7118 - CNRS
 * Cyril Pestel / ATILF UMR 7118 - CNRS
 * Marie Tonnelier / ATILF UMR 7118 - CNRS
 * Ulrike Fleury / ATILF UMR 7118 - CNRS
 * Frdric Pierre / ATILF UMR 7118 - CNRS
 * Cline Moro / ATILF UMR 7118 - CNRS
 *  
 * This work is based on work done in the equipex ORTOLANG (http://www.ortolang.fr/), by several Ortolang contributors (mainly CNRTL and SLDR)
 * ORTOLANG is funded by the French State program "Investissements d'Avenir" ANR-11-EQPX-0032
 * %%
 * Copyright (C) 2013 - 2016 Ortolang Team
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

import fr.ortolang.diffusion.client.OrtolangClient;
import fr.ortolang.diffusion.client.OrtolangClientException;
import fr.ortolang.diffusion.client.account.OrtolangClientAccountException;

import org.apache.commons.cli.*;
import org.apache.commons.io.IOUtils;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings("ALL")
public class CheckBagCommand extends Command {

    public static final Pattern ORTOLANG_KEY_MATCHER = Pattern.compile("\\$\\{([\\w\\d:\\-_]*)\\}");

    private Options options = new Options();
    private StringBuilder errors = new StringBuilder();
    private StringBuilder fixed = new StringBuilder();
    private boolean fix = false;
    private OrtolangClient client;

    public CheckBagCommand() {
        options.addOption("h", "help", false, "show help.");
        options.addOption("U", "username", true, "username for login");
        options.addOption("P", "password", true, "password for login");
        options.addOption("p", "path", true, "path of the bag root");
        options.addOption("f", "fix", false, "fix problems (WARNING may delete some files)");
    }

    @Override
    public void execute(String[] args) {
        CommandLineParser parser = new DefaultParser();
        CommandLine cmd;
        String root = "";
        try {
            cmd = parser.parse(options, args);
            if (cmd.hasOption("h")) {
                help();
            }

            if (cmd.hasOption("f")) {
                fix = true;
            }

            if (cmd.hasOption("p")) {
                root = cmd.getOptionValue("p");
            } else {
                help();
            }

            String[] credentials = getCredentials(cmd);
            String username = credentials[0];
            String password = credentials[1];

            client = OrtolangClient.getInstance();
            if (username.length() > 0) {
                client.getAccountManager().setCredentials(username, password);
                client.login(username);
            }
            System.out.println("Connected as user: " + client.connectedProfile());

            if (!Files.exists(Paths.get(root))) {
                errors.append("-> Le chemin de base (").append(root).append(") n'existe pas\r\n");
            } else {
                if (!Files.exists(Paths.get(root, "data", "publication.properties"))) {
                    errors.append("-> publication.properties NOT found\r\n");
                }
                if (!Files.exists(Paths.get(root, "data", "workspace.properties"))) {
                    errors.append("-> workspace.properties NOT found\r\n");
                } else {
                    checkWorkspaceProperties(Paths.get(root, "data", "workspace.properties"));
                }

                if (Files.exists(Paths.get(root, "data", "snapshots"))) {
                    Files.list(Paths.get(root, "data", "snapshots")).forEach(this::checkSnapshotMetadata);
                    Files.list(Paths.get(root, "data", "snapshots")).forEach(this::checkPermissions);
                }

                if (Files.exists(Paths.get(root, "data", "head"))) {
                    checkSnapshotMetadata(Paths.get(root, "data", "head"));
                }
            }
            if (errors.length() > 0) {
                System.out.println("## Some errors has been found : ");
                System.out.print(errors.toString());
                if (fix) {
                    System.out.println("## Some errors has been fixed : ");
                    System.out.print(fixed.toString());
                }
            } else {
                System.out.println("No error found.");
            }

        } catch (ParseException | IOException e) {
            System.out.println("Failed to parse command line properties: " + e.getMessage());
            help();
        } catch (OrtolangClientException | OrtolangClientAccountException e) {
            System.out.println("Unexpected error !!");
            e.printStackTrace();
        }
    }

    private void checkSnapshotMetadata(Path root) {
        Path metadata = Paths.get(root.toString(), "metadata");
        try {
            Files.walkFileTree(metadata, new FileVisitor<Path>() {

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Path target = Paths.get(root.toString(), "objects",
                            metadata.relativize(file.getParent()).toString());
                    if (!Files.exists(target)) {
                        errors.append("-> unexisting target for metadata: ").append(file).append("\r\n");
                        if (fix) {
                            try {
                                Files.delete(file);
                                fixed.append("-> deleted metadata: ").append(file).append("\r\n");
                            } catch (IOException e) {
                                errors.append("-> unable to fix: ").append(e.getMessage()).append("\r\n");
                            }
                        }
                    } else if (file.endsWith("ortolang-item-json")) {
                        checkOrtolangItemJson(file);
                    }
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

            });
        } catch (IOException e) {
            System.out.println("Unable to walk file tree: " + e.getMessage());
        }
    }

    private void checkPermissions(Path root) {
        Path metadata = Paths.get(root.toString(), "metadata");
        Path objects = Paths.get(root.toString(), "objects");
        try {
            checkPathPermissions(objects, metadata, -1, new HashSet<>());
        } catch (IOException e) {
            System.out.println("Unable to walk file tree: " + e.getMessage());
        }
    }

    private void checkPathPermissions(Path node, Path nodeMD, int parentLevel, Set<Path> treatedNodes)
            throws IOException {
        int nodeLevel = parentLevel;
        if (!treatedNodes.contains(node)) {
            if (Files.exists(nodeMD)) {
                Path permissionMD = Paths.get(nodeMD.toString(), "ortolang-acl-json");
                if (Files.exists(permissionMD) && !Files.isDirectory(permissionMD)) {
                    nodeLevel = parseACLLevel(permissionMD);
                    if (nodeLevel < parentLevel) {
                        errors.append("-> unconsistent file acl permission for object: ").append(node)
                                .append("\r\n");
                        if (fix) {
                            Path aclParent = Paths.get(nodeMD.getParent().toString(), "ortolang-acl-json");
                            try (OutputStream os = Files.newOutputStream(aclParent)) {
                                Template tpl = Template.findTemplateByLevel(nodeLevel);
                                IOUtils.write(tpl.getJson(), os, "UTF-8");
                                os.flush();
                                //noinspection StringConcatenationInsideStringBufferAppend
                                fixed.append("-> new acl [").append(tpl.getName())
                                        .append("] set for parent of object: ").append(node).append("\r\n");
                            }
                        }
                        for (String sibling : node.getParent().toFile().list()) {
                            if (!sibling.equals(node.toString())) {
                                Path aclSibling = Paths.get(sibling, "ortolang-acl-json");
                                if (!Files.exists(aclSibling)) {
                                    if (fix) {
                                        try (OutputStream os = Files.newOutputStream(aclSibling)) {
                                            Template tpl = Template.findTemplateByLevel(nodeLevel);
                                            IOUtils.write(tpl.getJson(), os, "UTF-8");
                                            os.flush();
                                            //noinspection StringConcatenationInsideStringBufferAppend
                                            fixed.append("-> new acl [").append(tpl.getName())
                                                    .append("] set for sibling of object: ").append(node)
                                                    .append("\r\n");
                                        }
                                    }
                                    treatedNodes.add(node);
                                }
                            }
                        }
                    }
                }
            }
        }
        treatedNodes.add(node);
        if (Files.isDirectory(node)) {
            for (String child : node.toFile().list()) {
                checkPathPermissions(Paths.get(node.toString(), child), Paths.get(nodeMD.toString(), child),
                        (nodeLevel < 0 ? 0 : nodeLevel), treatedNodes);
            }
        }
    }

    private int parseACLLevel(Path aclFile) throws IOException {
        JsonReader reader = Json.createReader(Files.newInputStream(aclFile));
        String name = reader.readObject().getString("template");
        return Template.findTemplateByName(name).getLevel();
    }

    private void checkWorkspaceProperties(Path workspaceFilePath) throws IOException {
        Properties props = new Properties();
        InputStream in = Files.newInputStream(workspaceFilePath);
        props.load(in);
        //TODO check whether workspace alias is availabled
        String owner = props.getProperty("owner");
        if (owner != null) {
            checkObject(owner, "owner");
        }
        String members = props.getProperty("members");
        if (members != null) {
            for (String member : members.split(",")) {
                checkObject(member, "member");
            }
        }
    }

    private void checkOrtolangItemJson(Path filepath) {
        String jsonContent = getContent(filepath);
        if (jsonContent != null) {
            List<String> keys = extractOrtolangKeys(jsonContent);

            System.out.println("Looking for keys in registry : " + keys);
            keys.parallelStream().forEach((key) -> checkObject(key, "referential"));
        }
    }

    private void checkObject(String key, String subject) {
        try {
            JsonObject object = client.getObject(key);
            if (object == null) {
                errors.append("-> ").append(subject).append(" ").append(key).append(" doesn't exist\r\n");
            }
        } catch (OrtolangClientException | OrtolangClientAccountException e) {
            errors.append("-> unable to find ").append(key).append(" : ").append(e.getMessage()).append("\r\n");
        }
    }

    public static List<String> extractOrtolangKeys(String json) {
        Matcher okMatcher = CheckBagCommand.ORTOLANG_KEY_MATCHER.matcher(json);
        List<String> ortolangKeys = new ArrayList<>();
        while (okMatcher.find()) {
            if (!ortolangKeys.contains(okMatcher.group(1))) {
                ortolangKeys.add(okMatcher.group(1));
            }
        }
        return ortolangKeys;
    }

    public static final String getContent(Path filepath) {
        String content = null;
        try (InputStream is = Files.newInputStream(filepath)) {
            content = IOUtils.toString(is, "UTF-8");
        } catch (IOException e) {
            System.out.println("  unable to get content of file : " + filepath + " : " + e.getMessage());
        }
        return content;
    }

    private void help() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("Check Bag", options);
        System.exit(0);
    }

    public static class Template {

        static Map<String, Template> templates = new HashMap<>();

        static {
            templates.put("forall", new Template(0, "forall", "{\"template\":\"forall\"}"));
            templates.put("authentified", new Template(1, "authentified", "{\"template\":\"authentified\"}"));
            templates.put("esr", new Template(2, "esr", "{\"template\":\"esr\"}"));
            templates.put("restricted", new Template(3, "restricted", "{\"template\":\"restricted\"}"));
        }

        private int level;
        private String name;
        private String json;

        private Template(int level, String name, String json) {
            this.level = level;
            this.name = name;
            this.json = json;
        }

        public int getLevel() {
            return level;
        }

        public String getName() {
            return name;
        }

        public String getJson() {
            return json;
        }

        public static Template findTemplateByName(String name) {
            return templates.get(name);
        }

        public static Template findTemplateByLevel(int level) {
            for (Template template : templates.values()) {
                if (template.getLevel() == level) {
                    return template;
                }
            }
            return templates.get("forall");
        }

    }

}