com.houghtonassociates.bamboo.plugins.view.GitWebRepositoryViewer.java Source code

Java tutorial

Introduction

Here is the source code for com.houghtonassociates.bamboo.plugins.view.GitWebRepositoryViewer.java

Source

/**
 * Copyright 2012 Houghton Associates
 * 
 * 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 com.houghtonassociates.bamboo.plugins.view;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.atlassian.bamboo.collections.ActionParametersMap;
import com.atlassian.bamboo.commit.Commit;
import com.atlassian.bamboo.commit.CommitFile;
import com.atlassian.bamboo.repository.RepositoryData;
import com.atlassian.bamboo.repository.RepositoryDefinition;
import com.atlassian.bamboo.repository.RepositoryException;
import com.atlassian.bamboo.resultsummary.ResultsSummary;
import com.atlassian.bamboo.resultsummary.vcs.RepositoryChangeset;
import com.atlassian.bamboo.util.UrlUtils;
import com.atlassian.bamboo.utils.error.ErrorCollection;
import com.atlassian.bamboo.variable.CustomVariableContext;
import com.atlassian.bamboo.webrepository.AbstractWebRepositoryViewer;
import com.atlassian.bamboo.webrepository.CommitUrlProvider;
import com.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfiguration;
import com.houghtonassociates.bamboo.plugins.GerritRepositoryAdapter;
import com.houghtonassociates.bamboo.plugins.dao.GerritChangeVO;

/**
 * GitWeb Repository Browser Integration.
 * 
 * @author Jason Huntley
 * 
 */
public class GitWebRepositoryViewer extends AbstractWebRepositoryViewer implements CommitUrlProvider {

    private static final String FULL_COMMIT_VIEW_TEMPLATE = "gitwebViewOfCommits.ftl";
    private static final String SUMMARY_COMMIT_VIEW_TEMPLATE = "gitwebViewOfCommitsSummary.ftl";

    protected static final String GITWEB_REPOSITORY_URL = "webRepository.gitwebRepositoryViewer.webRepositoryUrl";
    protected static final String GITWEB_REPOSITORY_NAME = "webRepository.gitwebRepositoryViewer.webRepositoryRepoName";
    protected static final String GITWEB_REPOSITORY_PATH = "webRepository.gitwebRepositoryViewer.webRepositoryPath";

    private static final String GITWEB_PROJCT = "?p=";
    private static final String GITWEB_ACTION_COMMIT_HIST = ";a=commit;h=";
    private static final String GITWEB_ACTION_COMMIT_DIFF = ";a=commitdiff;h=";
    private static final String GITWEB_ACTION_HISTORY = ";a=history;";
    private static final String GITWEB_ACTION_BLOB = ";a=blob";
    private static final String GITWEB_FILE = ";f=";
    private static final String GITWEB_FILE_HIST = ";h=";
    private static final String GITWEB_FILE_BHIST = ";hb=";

    private static final String BAMBOO_REV = "revision=";

    private static final String GIT_COMMIT_ACTION = "/COMMIT_MSG";

    private String webRepositoryUrl;
    private String webRepositoryPath;
    private String webRepositoryRepoName;

    private transient CustomVariableContext customVariableContext;

    private final Logger logger = Logger.getLogger(GitWebRepositoryViewer.class);

    private boolean gerritOnline = true;

    // For speeding up consecutive searches in a session
    private Map<String, String> changeIDtoRev = new HashMap<String, String>();

    @Override
    public void populateFromParams(@NotNull ActionParametersMap params) {
        setWebRepositoryUrl(params.getString(GITWEB_REPOSITORY_URL));
        setWebRepositoryRepoName(params.getString(GITWEB_REPOSITORY_NAME));
        setWebRepositoryPath(params.getString(GITWEB_REPOSITORY_PATH));
    }

    @Override
    public void populateFromConfig(@NotNull HierarchicalConfiguration config) {
        setWebRepositoryUrl(config.getString(GITWEB_REPOSITORY_URL));
        setWebRepositoryRepoName(config.getString(GITWEB_REPOSITORY_NAME));
        setWebRepositoryPath(config.getString(GITWEB_REPOSITORY_PATH));
    }

    @NotNull
    @Override
    public HierarchicalConfiguration toConfiguration() {
        HierarchicalConfiguration configuration = super.toConfiguration();
        configuration.setProperty(GITWEB_REPOSITORY_NAME, getWebRepositoryRepoName());
        configuration.setProperty(GITWEB_REPOSITORY_PATH, getWebRepositoryPath());
        configuration.setProperty(GITWEB_REPOSITORY_URL,
                com.atlassian.bamboo.util.UrlUtils.correctlyFormatUrl(getWebRepositoryUrl()));
        return configuration;
    }

    @NotNull
    @Override
    public ErrorCollection validate(@NotNull BuildConfiguration buildConfiguration) {
        ErrorCollection errorCollection = super.validate(buildConfiguration);

        String webRepositoryUrl = buildConfiguration.getString(GITWEB_REPOSITORY_URL);
        if (StringUtils.isBlank(webRepositoryUrl)) {
            errorCollection.addError(GITWEB_REPOSITORY_URL, "Please specify the url for your GITWEB instance.");
        }

        webRepositoryUrl = customVariableContext.substituteString(webRepositoryUrl);
        if (!StringUtils.isBlank(webRepositoryUrl)
                && !com.opensymphony.util.UrlUtils.verifyHierachicalURI(webRepositoryUrl)) {
            errorCollection.addError(GITWEB_REPOSITORY_URL, "This is not a valid url");
        }

        String webRepositoryName = buildConfiguration.getString(GITWEB_REPOSITORY_NAME);
        if (StringUtils.isBlank(webRepositoryName)) {
            errorCollection.addError(GITWEB_REPOSITORY_NAME,
                    "Please specify the name of the repository on your GITWEB instance.");
        }

        return errorCollection;
    }

    private boolean isChangeID(String id) {
        // I0000000000000000000000000000000000000000
        if (id.startsWith("I") && id.substring(1).length() == 40) {
            return true;
        }

        return false;
    }

    private String resolveIfChangeID(RepositoryData rd, String id) {
        String newRevision = id;
        if (rd.getRepository() instanceof GerritRepositoryAdapter) {
            GerritRepositoryAdapter gra = (GerritRepositoryAdapter) rd.getRepository();

            GerritChangeVO change = null;

            if (isChangeID(id)) {
                if (gerritOnline) {
                    try {
                        // For speeding up consecutive searches in a session
                        newRevision = changeIDtoRev.get(id);

                        if (newRevision == null || newRevision.isEmpty()) {
                            logger.info("Resolving ID...");
                            change = gra.getGerritDAO().getChangeByID(id);
                            newRevision = change.getLastRevision();
                            changeIDtoRev.put(id, newRevision);
                        } else {
                            logger.info("Using previously resolved revision.");
                        }
                    } catch (RepositoryException e) {
                        logger.error("Failed to load change ID!");
                        logger.error(e.getMessage());
                        gerritOnline = false;
                        return null;
                    }
                } else {
                    logger.error("Gerrit is Offline!");
                    return null;
                }
            }
        }

        return newRevision;
    }

    /**
     * Build the GitWeb URL for browsing source
     * 
     * @param rd
     *            - Repository Data
     * @param rev
     *            - hash id
     * 
     * @return
     * @throws RepositoryException
     */
    private String buildWebURL(RepositoryData rd, String rev) {
        String url = null;

        if (isChangeID(rev)) {
            rev = resolveIfChangeID(rd, rev);

            if (rev != null)
                url = buildDefaultGitWebURL(rev);
        } else {
            url = buildDefaultGitWebURL(rev);
        }

        return url;
    }

    private String buildDefaultGitWebURL(String rev) {
        if (rev == null)
            return null;

        return buildProjectURL() + GITWEB_ACTION_COMMIT_HIST + rev;
    }

    private String buildProjectURL() {
        StringBuilder result = new StringBuilder();
        if (StringUtils.isBlank(webRepositoryUrl)) {
            logger.warn("Web url is not defined. Can not generate web repository urls for file.");
            return null;
        }

        String substitutedWebUrl = customVariableContext.substituteString(webRepositoryUrl);
        if (substitutedWebUrl == null) {
            logger.warn("Variable substitution failed for web url '" + webRepositoryUrl + "', using original url.");
            substitutedWebUrl = webRepositoryUrl;
        }

        result.append(UrlUtils.appendSlashIfDoesntExist(substitutedWebUrl));

        String substitutedRepoName = customVariableContext.substituteString(webRepositoryRepoName);
        if (substitutedRepoName == null) {
            logger.warn("Variable substitution failed for web name '" + webRepositoryRepoName
                    + "', using original name.");
            substitutedRepoName = webRepositoryRepoName;
        }

        result.append(GITWEB_PROJCT + substitutedRepoName);

        return result.toString();
    }

    public String getHtmlForCommitsFull(ResultsSummary resultsSummary, RepositoryChangeset repositoryChangeset,
            RepositoryDefinition repositoryData) {
        final Map<String, Object> context = new HashMap<String, Object>();

        context.put("buildResultsSummary", resultsSummary);
        context.put("repositoryChangeset", repositoryChangeset);
        context.put("repositoryData", repositoryData);
        context.put("linkGenerator", this);

        return templateRenderer.render(FULL_COMMIT_VIEW_TEMPLATE, context);
    }

    public String getHtmlForCommitsSummary(ResultsSummary resultsSummary, RepositoryChangeset repositoryChangeset,
            RepositoryDefinition repositoryData, int maxChanges) {
        final Map<String, Object> context = new HashMap<String, Object>();

        context.put("buildResultsSummary", resultsSummary);
        context.put("repositoryChangeset", repositoryChangeset);
        context.put("repositoryData", repositoryData);
        context.put("linkGenerator", this);
        context.put("maxChanges", maxChanges);

        String result = templateRenderer.render(SUMMARY_COMMIT_VIEW_TEMPLATE, context);

        return result;
    }

    public String getHtmlForCommitsSummary(ResultsSummary resultsSummary, RepositoryChangeset repositoryChangeset,
            RepositoryDefinition repositoryDefinition) {
        final Map<String, Object> context = new HashMap<String, Object>();

        context.put("buildResultsSummary", resultsSummary);
        context.put("repositoryChangeset", repositoryChangeset);
        context.put("repositoryData", (RepositoryData) repositoryDefinition);
        context.put("linkGenerator", this);
        context.put("maxChanges", 2);

        return templateRenderer.render(SUMMARY_COMMIT_VIEW_TEMPLATE, context);
    }

    /**
     * Generates the revision string for the build summary and changes tab
     * Example: Revision 102856bbc6439fcb9ed35a20469b2f0a48eff99e
     * 
     * @param commits
     * @param repositoryData
     * @return
     */
    public Map<Commit, String> getWebRepositoryUrlForCommits(Collection<Commit> commits,
            RepositoryData repositoryData) {
        Map<Commit, String> commitsToUrls = new HashMap<Commit, String>();

        if (webRepositoryUrl == null) {
            logger.warn("Web url is not defined. Can not generate web repository urls for file.");
        } else {
            for (Commit commit : commits) {
                String result = "";
                final String cs = commit.guessChangeSetId();

                if (cs != null) {
                    result = buildWebURL(repositoryData, cs);
                    commitsToUrls.put(commit, result);
                }
            }
        }

        return commitsToUrls;
    }

    @Override
    public String getWebRepositoryUrlForCommit(Commit commit, RepositoryData repositoryData) {
        Map<Commit, String> commitsToUrls = getWebRepositoryUrlForCommits(Arrays.asList(commit), repositoryData);
        return commitsToUrls.isEmpty() ? null : commitsToUrls.get(commit);
    }

    /**
     * Pulls back version url for each file listed under changes.
     * 
     * @param revisions
     * @param repositoryData
     * @return
     */
    public Map<String, String> getWebRepositoryUrlForRevisions(Collection<String> revisions,
            RepositoryData repositoryData) {
        Map<String, String> commitsToUrls = new HashMap<String, String>();

        if (webRepositoryUrl == null) {
            logger.warn("Web url is not defined. Can not generate web repository urls for file.");
        } else {
            for (String revision : revisions) {
                String newRev = new String(revision);
                String result = "";

                // Sometimes format comes back as CommitFile.toString(), not
                // sure why Bamboo is doing this.
                // com.atlassian.bamboo.commit.CommitFileImpl@9d6dbc1[name=src/com/houghtonassociates/DemoGitMatt.java,revision=102856bbc6439fcb9ed35a20469b2f0a48eff99e]

                if (revision != null) {
                    if (revision.length() > 40) {
                        int index = revision.lastIndexOf(BAMBOO_REV);
                        newRev = revision.substring(index + BAMBOO_REV.length(), revision.length() - 1);

                    }

                    result = buildWebURL(repositoryData, newRev);
                    commitsToUrls.put(revision, result);
                }
            }
        }

        return commitsToUrls;
    }

    @Override
    public String getWebRepositoryUrlForRevision(String revisionId, RepositoryData repositoryData) {
        Map<String, String> revisionsToUrls = getWebRepositoryUrlForRevisions(Arrays.asList(revisionId),
                repositoryData);
        return revisionsToUrls.isEmpty() ? null : revisionsToUrls.get(revisionId);
    }

    /**
     * Pulls back gitweb file urls for each file listed under changes.
     * 
     * @param revisions
     * @param repositoryData
     * @return
     */
    @Nullable
    public String getWebRepositoryUrlForFile(@NotNull CommitFile file, RepositoryData repositoryData) {
        StringBuilder result = new StringBuilder();
        String revision = file.getRevision();

        if (file.getName().equals(GIT_COMMIT_ACTION))
            return null;

        revision = this.resolveIfChangeID(repositoryData, revision);

        if (revision == null)
            return null;

        if (StringUtils.isBlank(webRepositoryUrl)) {
            logger.warn("Web url is not defined. Can not generate web repository urls for file.");
            return null;
        }

        result.append(buildProjectURL());

        String substitutedPathToRemove = customVariableContext.substituteString(webRepositoryPath);
        String fileName = file.getName();
        if (substitutedPathToRemove != null) {
            substitutedPathToRemove = UrlUtils.stripLeadingSlashes(substitutedPathToRemove);
            fileName = UrlUtils.stripLeadingSlashes(fileName);
            if (fileName.startsWith(substitutedPathToRemove)) {
                fileName = fileName.substring(substitutedPathToRemove.length());
            }
        }

        fileName = UrlUtils.stripLeadingSlashes(fileName);

        result.append(GITWEB_ACTION_BLOB + GITWEB_FILE);
        result.append(fileName);

        result.append(GITWEB_FILE_BHIST);
        result.append(revision);

        return result.toString();
    }

    @Nullable
    public String getWebRepositoryUrlForDiff(CommitFile file, RepositoryData repositoryData) {
        StringBuilder result = new StringBuilder();
        String revision = file.getRevision();

        if (file.getName().equals(GIT_COMMIT_ACTION))
            return null;

        revision = this.resolveIfChangeID(repositoryData, revision);

        if (revision == null)
            return null;

        if (file.isRevisionKnown()) {
            revision = resolveIfChangeID(repositoryData, revision);

            if (revision != null) {
                result.append(this.buildProjectURL() + GITWEB_ACTION_COMMIT_DIFF + revision);
            }
        }

        return result.toString();
    }

    public String getWebRepositoryUrl() {
        return webRepositoryUrl;
    }

    public void setWebRepositoryUrl(@Nullable String webRepositoryUrl) {
        this.webRepositoryUrl = webRepositoryUrl == null ? null : webRepositoryUrl.trim();
    }

    public String getWebRepositoryPath() {
        return webRepositoryPath;
    }

    public void setWebRepositoryPath(@Nullable String webRepositoryPath) {
        this.webRepositoryPath = webRepositoryPath == null ? null : webRepositoryPath.trim();
    }

    public String getWebRepositoryRepoName() {
        return webRepositoryRepoName;
    }

    public void setWebRepositoryRepoName(@Nullable String webRepositoryRepoName) {
        this.webRepositoryRepoName = webRepositoryRepoName == null ? null : webRepositoryRepoName.trim();
    }

    public void setCustomVariableContext(final CustomVariableContext customVariableContext) {
        this.customVariableContext = customVariableContext;
    }

    @Override
    public String getHtmlForCommitsFull(@NotNull final ResultsSummary resultsSummary,
            @NotNull final RepositoryChangeset repositoryChangeset, @NotNull final RepositoryData repositoryData) {
        final Map<String, Object> context = new HashMap<String, Object>();
        context.put("buildResultsSummary", resultsSummary);
        context.put("repositoryChangeset", repositoryChangeset);
        context.put("repositoryData", repositoryData);
        context.put("linkGenerator", this);

        return templateRenderer.render(FULL_COMMIT_VIEW_TEMPLATE, context);
    }

    @Override
    public String getHtmlForCommitsSummary(@NotNull final ResultsSummary resultsSummary,
            @NotNull final RepositoryChangeset repositoryChangeset, @NotNull final RepositoryData repositoryData,
            final int maxChanges) {
        final Map<String, Object> context = new HashMap<String, Object>();
        context.put("buildResultsSummary", resultsSummary);
        context.put("repositoryChangeset", repositoryChangeset);
        context.put("repositoryData", repositoryData);
        context.put("linkGenerator", this);
        context.put("maxChanges", maxChanges);

        return templateRenderer.render(SUMMARY_COMMIT_VIEW_TEMPLATE, context);
    }
}