org.craftercms.studio.impl.v1.deployment.EnvironmentStoreGitDeployer.java Source code

Java tutorial

Introduction

Here is the source code for org.craftercms.studio.impl.v1.deployment.EnvironmentStoreGitDeployer.java

Source

/*
 * Crafter Studio Web-content authoring solution
 * Copyright (C) 2007-2016 Crafter Software Corporation.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.craftercms.studio.impl.v1.deployment;

import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;

import org.apache.commons.io.IOUtils;
import org.craftercms.studio.api.v1.deployment.Deployer;
import org.craftercms.studio.api.v1.log.Logger;
import org.craftercms.studio.api.v1.log.LoggerFactory;
import org.craftercms.studio.api.v1.service.content.ContentService;
import org.craftercms.studio.api.v1.service.deployment.ContentNotFoundForPublishingException;
import org.craftercms.studio.api.v1.service.deployment.UploadFailedException;
import org.eclipse.jgit.api.ApplyResult;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.io.SafeBufferedOutputStream;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.Set;

public class EnvironmentStoreGitDeployer implements Deployer {

    private final static Logger logger = LoggerFactory.getLogger(EnvironmentStoreGitDeployer.class);

    @Override
    public void deployFile(String site, String path) {
        try (Repository envStoreRepo = getEnvironmentStoreRepositoryInstance(site)) {
            fetchFromRemote(site, envStoreRepo);
            InputStream patch = createPatch(envStoreRepo, site, path);
            Git git = new Git(envStoreRepo);
            ApplyResult result = git.apply().setPatch(patch).call();
            git.add().addFilepattern(".").call();
            git.commit().setMessage("deployment").call();

        } catch (/*IOException | GitAPIException | */Exception e) {
            logger.error("Error while deploying file for site: " + site + " path: " + path, e);
        }
    }

    private void fetchFromRemote(String site, Repository repository) {
        try (Git git = new Git(repository)) {
            Path siteRepoPath = Paths.get(rootPath, "sites", site, ".git");
            Collection<Ref> refs = Git.lsRemoteRepository().setHeads(true).setTags(true)
                    .setRemote(siteRepoPath.toAbsolutePath().toString()).call();
            FetchResult result = git.fetch().setRemote("work-area").setCheckFetchedObjects(true).call();

        } catch (GitAPIException e) {
            logger.error(
                    "Error while fetching updates for repository: " + repository.getDirectory().getAbsolutePath(),
                    e);
        }
    }

    private InputStream createPatch(Repository repository, String site, String path) {
        try (Git git = new Git(repository)) {

            // the diff works on TreeIterators, we prepare two for the two branches
            AbstractTreeIterator oldTreeParser = prepareTreeParser(repository, "refs/heads/master");
            AbstractTreeIterator newTreeParser = prepareTreeParser(repository, "FETCH_HEAD");//"refs/remotes/work-area/master");

            // then the procelain diff-command returns a list of diff entries
            List<DiffEntry> diff = git.diff().setOldTree(oldTreeParser).setNewTree(newTreeParser)
                    .setPathFilter(getTreeFilter(path)).call();

            //PipedInputStream pin = new PipedInputStream();
            OutputStream out = new ByteArrayOutputStream();

            for (DiffEntry diffEntry : diff) {
                DiffEntry.ChangeType ct = diffEntry.getChangeType();
                DiffFormatter df = new DiffFormatter(out);
                FileHeader fh = df.toFileHeader(diffEntry);
                if (fh.getPatchType().equals(FileHeader.PatchType.BINARY)) {
                    logger.error("ERRRRRRRRRRROR");
                }
            }

            //OutputStream os = new FileOutputStream("/Users/dejanbrkic/gitpatchtest.diff");
            DiffFormatter df = new DiffFormatter(out);
            df.setRepository(repository);
            //df.setBinaryFileThreshold();
            df.setPathFilter(getTreeFilter(path));
            df.setAbbreviationLength(OBJECT_ID_STRING_LENGTH);
            df.format(diff);
            df.flush();
            df.close();
            String content = out.toString();
            logger.error("++++++++++++++++");
            logger.error(content);
            logger.error("++++++++++++++++");
            InputStream in = IOUtils.toInputStream(content);
            return in;
        } catch (GitAPIException | IOException e) {
            logger.error("Error while creating patch for site: " + site + " path: " + path, e);
        }
        return null;
    }

    private TreeFilter getTreeFilter(String path) {
        TreeFilter tf = PathFilterGroup.createFromStrings(getGitPath(path));
        return tf;
    }

    private AbstractTreeIterator prepareTreeParser(Repository repository, String ref)
            throws IOException, MissingObjectException, IncorrectObjectTypeException {
        // from the commit we can build the tree which allows us to construct the TreeParser
        Ref head = repository.exactRef(ref);
        try (RevWalk walk = new RevWalk(repository)) {
            RevCommit commit = walk.parseCommit(head.getObjectId());
            RevTree tree = walk.parseTree(commit.getTree().getId());

            CanonicalTreeParser oldTreeParser = new CanonicalTreeParser();
            try (ObjectReader oldReader = repository.newObjectReader()) {
                oldTreeParser.reset(oldReader, tree.getId());
            }

            walk.dispose();

            return oldTreeParser;
        }
    }

    private Repository getEnvironmentStoreRepositoryInstance(String site) throws IOException {

        Path siteRepoPath = Paths.get(environmentsStoreRootPath, site, environment, ".git").toAbsolutePath();
        if (!Files.exists(siteRepoPath)) {
            createEnvironmentStoreRepository(site);
        }
        Repository envStoreRepo = openGitRepository(siteRepoPath);
        if (!checkIfWorkAreaAddedAsRemote(envStoreRepo)) {
            addWorkAreaRemote(site, envStoreRepo);
        }
        return envStoreRepo;
    }

    private boolean createEnvironmentStoreRepository(String site) {
        boolean success = true;
        Path siteEnvironmentStoreRepoPath = Paths.get(environmentsStoreRootPath, site, environment);
        try {
            Files.deleteIfExists(siteEnvironmentStoreRepoPath);
            siteEnvironmentStoreRepoPath = Paths.get(environmentsStoreRootPath, site, environment, ".git");
            Repository repository = FileRepositoryBuilder.create(siteEnvironmentStoreRepoPath.toFile());
            repository.create();

            Git git = new Git(repository);
            git.add().addFilepattern(".").call();
            RevCommit commit = git.commit().setMessage("initial content").setAllowEmpty(true).call();
        } catch (IOException | GitAPIException e) {
            logger.error("Error while creating repository for site " + site, e);
            success = false;
        }
        return success;
    }

    private void addWorkAreaRemote(String site, Repository envStoreRepo) {
        envStoreRepo.getRemoteName("work-area");
        Git git = new Git(envStoreRepo);
        StoredConfig config = git.getRepository().getConfig();
        Path siteRepoPath = Paths.get(rootPath, "sites", site, ".git");
        config.setString("remote", "work-area", "url", siteRepoPath.toAbsolutePath().toString());
        try {
            config.save();
        } catch (IOException e) {
            logger.error("Error adding work area as remote for environment store.", e);
        }
    }

    private boolean checkIfWorkAreaAddedAsRemote(Repository repository) {
        boolean exists = false;
        try {
            Config storedConfig = repository.getConfig();
            Set<String> remotes = storedConfig.getSubsections("remote");

            for (String remoteName : remotes) {
                logger.debug("Remote: " + remoteName);
                if (remoteName.equals("work-area")) {
                    exists = true;
                    break;
                }
            }
        } catch (Exception err) {
            logger.error("Error while reading remotes info.", err);
        }
        return exists;
    }

    private RevTree getTree(Repository repository)
            throws AmbiguousObjectException, IncorrectObjectTypeException, IOException, MissingObjectException {
        ObjectId lastCommitId = repository.resolve(Constants.HEAD);

        // a RevWalk allows to walk over commits based on some filtering
        try (RevWalk revWalk = new RevWalk(repository)) {
            RevCommit commit = revWalk.parseCommit(lastCommitId);

            // and using commit's tree find the path
            RevTree tree = commit.getTree();
            return tree;
        }
    }

    private String getGitPath(String path) {
        String gitPath = path.replaceAll("/+", "/");
        gitPath = gitPath.replaceAll("^/", "");
        return gitPath;
    }

    private Repository getSiteRepositoryInstance(String site) throws IOException {

        Path siteRepoPath = Paths.get(rootPath, "sites", site, ".git");
        if (Files.exists(siteRepoPath)) {
            return openGitRepository(siteRepoPath);
        } else {
            Files.deleteIfExists(siteRepoPath);
            //return cloneRemoteRepository(siteConfiguration.getGitRepositoryUrl(), siteConfiguration.getLocalRepositoryRoot());
        }
        return null;
    }

    private Repository openGitRepository(Path repositoryPath) throws IOException {
        FileRepositoryBuilder builder = new FileRepositoryBuilder();
        Repository repository = builder.setGitDir(repositoryPath.toFile()).readEnvironment().findGitDir().build();
        return repository;
    }

    @Override
    public void deployFiles(String site, List<String> paths) {

    }

    @Override
    public void deployFiles(String site, List<String> paths, List<String> deletedFiles)
            throws ContentNotFoundForPublishingException, UploadFailedException {

    }

    @Override
    public void deleteFile(String site, String path) {

    }

    @Override
    public void deleteFiles(String site, List<String> paths) {

    }

    public String getEnvironmentsStoreRootPath() {
        return environmentsStoreRootPath;
    }

    public void setEnvironmentsStoreRootPath(String environmentsStoreRootPath) {
        this.environmentsStoreRootPath = environmentsStoreRootPath;
    }

    public ContentService getContentService() {
        return contentService;
    }

    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    public String getEnvironment() {
        return environment;
    }

    public void setEnvironment(String environment) {
        this.environment = environment;
    }

    public String getRootPath() {
        return rootPath;
    }

    public void setRootPath(String path) {
        rootPath = path;
    }

    protected String environmentsStoreRootPath;
    protected ContentService contentService;
    protected String environment;
    protected String rootPath;
}