org.jahia.services.templates.SourceControlHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.services.templates.SourceControlHelper.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     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/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must be used in accordance with the terms and conditions
 *     contained in a separate written agreement between you and Jahia Solutions Group SA.
 *
 *     If you are unsure which license is appropriate for your use,
 *     please contact the sales department at sales@jahia.com.
 */
package org.jahia.services.templates;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.dom4j.DocumentException;
import org.jahia.data.templates.JahiaTemplatesPackage;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.content.JCRNodeWrapper;
import org.jahia.services.content.JCRSessionWrapper;
import org.jahia.services.content.nodetypes.NodeTypeRegistry;
import org.jahia.settings.SettingsBean;
import org.jahia.utils.PomUtils;
import org.jahia.utils.i18n.Messages;
import org.jahia.utils.i18n.ResourceBundles;
import org.osgi.framework.BundleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;

import javax.jcr.RepositoryException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Utility class for SCM related module operations.
 * 
 * @author Sergiy Shyrkov
 */
public class SourceControlHelper {

    private static class ModuleInfo {
        String id;
        String groupId;
        File path;
        String version;
    }

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

    private static final NameFileFilter POM_XML_FILE_FILTER = new NameFileFilter("pom.xml");

    private SourceControlFactory sourceControlFactory;

    private TemplatePackageRegistry templatePackageRegistry;

    public JCRNodeWrapper checkoutModule(final File moduleSources, final String scmURI, final String branchOrTag,
            final String moduleId, final String version, final JCRSessionWrapper session)
            throws IOException, RepositoryException, BundleException {
        boolean newModule = moduleSources == null;
        File sources = ensureModuleSourceFolder(moduleSources);

        try {
            // checkout sources from SCM
            SourceControlManagement scm = sourceControlFactory.checkoutRepository(sources, scmURI, branchOrTag,
                    false);

            // verify the sources and found out module information
            ModuleInfo moduleInfo = getModuleInfo(sources, scmURI, moduleId, version, branchOrTag);
            if (templatePackageRegistry.containsId(moduleInfo.id)
                    && !moduleInfo.groupId.equals(templatePackageRegistry.lookupById(moduleInfo.id).getGroupId())) {
                FileUtils.deleteDirectory(sources);
                throw new ScmUnavailableModuleIdException("Cannot checkout module " + moduleInfo.id
                        + " because another module with the same artifactId exists", moduleInfo.id);
            }
            if (version != null && !version.equals(moduleInfo.version)) {
                FileUtils.deleteDirectory(sources);
                throw new ScmWrongVersionException("Sources don't match module version");
            }

            if (newModule) {
                File newPath = new File(sources.getParentFile(), moduleInfo.id + "_" + moduleInfo.version);
                int i = 0;
                while (newPath.exists()) {
                    newPath = new File(sources.getParentFile(),
                            moduleInfo.id + "_" + moduleInfo.version + "_" + (++i));
                }

                FileUtils.moveDirectory(sources, newPath);
                moduleInfo.path = new File(moduleInfo.path.getPath().replace(sources.getPath(), newPath.getPath()));
                sources = newPath;
                scm = sourceControlFactory.getSourceControlManagement(moduleInfo.path);
            }

            if (sources.equals(moduleInfo.path)) {
                setSCMConfigInPom(sources, scmURI);
            }

            JahiaTemplatesPackage pack = ServicesRegistry.getInstance().getJahiaTemplateManagerService()
                    .compileAndDeploy(moduleInfo.id, moduleInfo.path, session);
            if (pack != null) {
                JCRNodeWrapper node = session.getNode("/modules/" + pack.getIdWithVersion());
                pack.setSourceControl(scm);
                setSourcesFolderInPackageAndNode(pack, moduleInfo.path, node);
                session.save();

                // flush resource bundle cache
                ResourceBundles.flushCache();
                NodeTypeRegistry.getInstance().flushLabels();

                return node;
            } else {
                FileUtils.deleteDirectory(sources);
            }
        } catch (BundleException e) {
            FileUtils.deleteQuietly(sources);
            throw e;
        } catch (RepositoryException e) {
            FileUtils.deleteQuietly(sources);
            throw e;
        } catch (IOException e) {
            FileUtils.deleteQuietly(sources);
            throw e;
        } catch (DocumentException e) {
            FileUtils.deleteQuietly(sources);
            throw new IOException(e);
        } catch (XmlPullParserException e) {
            FileUtils.deleteQuietly(sources);
            throw new IOException(e);
        }

        return null;
    }

    public File checkoutTmpModule(String moduleId, String version, String scmURI, String branchOrTag)
            throws IOException, XmlPullParserException, DocumentException, RepositoryException {
        File sources = ensureModuleSourceFolder(null);
        sourceControlFactory.checkoutRepository(sources, scmURI, branchOrTag, false);
        ModuleInfo moduleInfo = getModuleInfo(sources, scmURI, moduleId, version, branchOrTag);
        if (version != null && !version.equals(moduleInfo.version)) {
            FileUtils.deleteDirectory(sources);
            throw new ScmWrongVersionException("Sources don't match module version");
        }
        return moduleInfo.path;
    }

    public boolean checkValidSources(JahiaTemplatesPackage pack, File sources) {
        if (!SettingsBean.getInstance().isDevelopmentMode()) {
            return false;
        }
        if (!new File(sources, "src/main/resources").exists() && !new File(sources, "src/main/webapp").exists()) {
            return false;
        }
        File pom = new File(sources, "pom.xml");
        if (pom.exists()) {
            try {
                String sourceVersion = PomUtils.getVersion(pom);
                if (sourceVersion != null && sourceVersion.equals(pack.getVersion().toString())) {
                    return true;
                }
                if (sourceVersion != null && sourceVersion.matches("\\$\\{.*\\}")) {
                    logger.warn(
                            "'version' contains an expression but should be a constant. Stopping mount of source folder.");
                }
            } catch (Exception e) {
                logger.error("Cannot parse pom.xml file at " + pom, e);
            }
        }
        return false;
    }

    private File ensureModuleSourceFolder(File moduleSources) throws IOException {
        File sources = moduleSources;
        if (sources == null) {
            sources = new File(SettingsBean.getInstance().getModulesSourcesDiskPath(),
                    UUID.randomUUID().toString());
        }

        if (sources.exists()) {
            throw new IOException("Sources folder " + sources + " already exist");
        }

        if (!sources.getParentFile().exists() && !sources.getParentFile().mkdirs()) {
            throw new IOException("Unable to create sources folder at: " + sources);
        }

        return sources;
    }

    private ModuleInfo getModuleInfo(final File sources, final String scmURI, final String moduleId,
            final String version, final String branchOrTag)
            throws IOException, DocumentException, XmlPullParserException {
        ModuleInfo info = new ModuleInfo();
        info.id = moduleId;
        info.path = sources;
        info.version = version;

        Model pom = null;

        if (!StringUtils.isEmpty(moduleId)) {
            // Find the root folder of the module inside the repository, if in a subfolder
            Collection<File> files = FileUtils.listFiles(sources, POM_XML_FILE_FILTER, TrueFileFilter.INSTANCE);
            for (File file : files) {
                Model modulePom = PomUtils.read(file);
                if (moduleId.equals(modulePom.getArtifactId())) {
                    // we've found our candidate
                    pom = modulePom;
                    info.path = file.getParentFile();
                    break;
                }
            }
        } else {
            pom = PomUtils.read(new File(sources, "pom.xml"));
        }
        if (pom != null) {
            info.id = pom.getArtifactId();
            info.groupId = pom.getGroupId();
            info.version = pom.getVersion();
            Parent parent = pom.getParent();
            if (parent != null) {
                String v = parent.getVersion();
                if (info.version == null) {
                    info.version = v;
                }

                if (info.groupId == null) {
                    info.groupId = parent.getGroupId();
                }
            }
        } else {
            FileUtils.deleteQuietly(sources);
            String msg = Messages.getInternalWithArguments("source.not.found.with.uri",
                    LocaleContextHolder.getLocale(), moduleId, StringUtils.defaultIfEmpty(version, ""), scmURI,
                    StringUtils.defaultIfEmpty(branchOrTag, ""));
            logger.error(msg);
            throw new IOException(msg);
        }

        return info;
    }

    public SourceControlFactory getSourceControlFactory() {
        return sourceControlFactory;
    }

    public File getSources(JahiaTemplatesPackage pack, JCRSessionWrapper session) throws RepositoryException {
        if (pack.getSourcesFolder() != null) {
            return pack.getSourcesFolder();
        }
        JCRNodeWrapper n = session.getNode("/modules/" + pack.getIdWithVersion());
        if (n.hasNode("j:versionInfo")) {
            JCRNodeWrapper vi = n.getNode("j:versionInfo");
            if (vi.hasProperty("j:sourcesFolder")) {
                File sources = new File(vi.getProperty("j:sourcesFolder").getString());

                if (checkValidSources(pack, sources)) {
                    pack.setSourcesFolder(sources);
                    // templatePackageRegistry.mountSourcesProvider(pack);
                    return sources;
                }
            }
        }
        return null;
    }

    public void sendToSourceControl(String moduleId, String scmURI, String scmType, JCRSessionWrapper session)
            throws RepositoryException, IOException {
        JahiaTemplatesPackage pack = templatePackageRegistry.lookupById(moduleId);
        String fullUri = "scm:" + scmType + ":" + scmURI;
        final File sources = getSources(pack, session);
        SourceControlManagement scm = sourceControlFactory.checkoutRepository(sources, fullUri, null, true);
        pack.setSourceControl(scm);
        setSCMConfigInPom(sources, fullUri);
        JCRNodeWrapper node = session.getNode("/modules/" + pack.getIdWithVersion());
        setSourcesFolderInPackageAndNode(pack, sources, node);
        session.save();
        scm.add(new File(sources, "pom.xml"));
        scm.commit("Initial commit");
    }

    public Map<String, String> listTags(String scmURI) throws IOException {
        return sourceControlFactory.listTags(scmURI);
    }

    public Map<String, String> listBranches(String scmURI) throws IOException {
        return sourceControlFactory.listBranches(scmURI);
    }

    public String guessBranchOrTag(String moduleVersion, String scm, Set<String> branchOrTags) {
        String[] splitVersion = StringUtils.split(StringUtils.removeEnd(moduleVersion, "-SNAPSHOT"), ".");
        if (moduleVersion.endsWith("-SNAPSHOT")) {
            String branch;
            if (splitVersion.length >= 3) {
                branch = String.format("JAHIA-%s-%s-%s-X-BRANCH", splitVersion);
                if (branchOrTags.contains(branch)) {
                    return branch;
                }
            }
            if (splitVersion.length >= 2) {
                branch = String.format("JAHIA-%s-%s-X-X-BRANCH", splitVersion);
                if (branchOrTags.contains(branch)) {
                    return branch;
                }
            }
            if (splitVersion.length >= 1) {
                branch = splitVersion[0] + "_x";
                if (branchOrTags.contains(branch)) {
                    return branch;
                }
            }
            if ("svn".equals(scm)) {
                branch = "trunk";
                if (branchOrTags.contains(branch)) {
                    return branch;
                }
            } else if ("git".equals(scm)) {
                branch = "master";
                if (branchOrTags.contains(branch)) {
                    return branch;
                }
            }
        } else {
            String tag = StringUtils.join(splitVersion, '_');
            if (branchOrTags.contains(tag)) {
                return tag;
            }
            tag = "JAHIA_" + tag;
            if (branchOrTags.contains(tag)) {
                return tag;
            }
        }
        return null;
    }

    private void setSCMConfigInPom(File sources, String uri) {
        try {
            PomUtils.updateScm(new File(sources, "pom.xml"), uri);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void setSourceControlFactory(SourceControlFactory sourceControlFactory) {
        this.sourceControlFactory = sourceControlFactory;
    }

    public void setSourcesFolderInPackage(JahiaTemplatesPackage pack, File sources) {
        if (checkValidSources(pack, sources)) {
            pack.setSourcesFolder(sources);
            try {
                SourceControlManagement sourceControlManagement = sourceControlFactory
                        .getSourceControlManagement(sources);
                if (sourceControlManagement != null) {
                    pack.setSourceControl(sourceControlManagement);
                }
            } catch (Exception e) {
                logger.error("Cannot get source control", e);
            }
        }
    }

    public void setSourcesFolderInPackageAndNode(JahiaTemplatesPackage pack, File sources, JCRNodeWrapper node)
            throws RepositoryException {
        setSourcesFolderInPackage(pack, sources);
        if (pack.getSourcesFolder() != null) {
            node.getNode("j:versionInfo").setProperty("j:sourcesFolder", pack.getSourcesFolder().getPath());
            if (pack.getSourceControl() != null) {
                try {
                    node.getNode("j:versionInfo").setProperty("j:scmURI", pack.getSourceControl().getURI());
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
    }

    public void setTemplatePackageRegistry(TemplatePackageRegistry registry) {
        templatePackageRegistry = registry;
    }
}