com.google.gitiles.DefaultAccess.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gitiles.DefaultAccess.java

Source

// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.google.gitiles;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;

import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.http.server.ServletUtils;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.resolver.FileResolver;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.util.IO;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

/**
 * Default implementation of {@link GitilesAccess} with local repositories.
 * <p>
 * Repositories are scanned on-demand under the given path, configured by
 * default from {@code gitiles.basePath}. There is no access control beyond what
 * user the JVM is running under.
 */
public class DefaultAccess implements GitilesAccess {
    private static final String ANONYMOUS_USER_KEY = "anonymous user";

    public static class Factory implements GitilesAccess.Factory {
        private final File basePath;
        private final String canonicalBasePath;
        private final String baseGitUrl;
        private final FileResolver<HttpServletRequest> resolver;

        Factory(File basePath, String baseGitUrl, FileResolver<HttpServletRequest> resolver) throws IOException {
            this.basePath = checkNotNull(basePath, "basePath");
            this.baseGitUrl = checkNotNull(baseGitUrl, "baseGitUrl");
            this.resolver = checkNotNull(resolver, "resolver");
            this.canonicalBasePath = basePath.getCanonicalPath();
        }

        @Override
        public GitilesAccess forRequest(HttpServletRequest req) {
            String path = req.getPathInfo();
            String repositoryPath;
            if (path == null || path == "/") {
                repositoryPath = null;
            } else {
                int slashPlus = path.indexOf("/+/");
                if (slashPlus >= 0) {
                    repositoryPath = path.substring(0, slashPlus);
                } else if (path.endsWith("/+")) {
                    repositoryPath = path.substring(0, path.length() - 2);
                } else {
                    repositoryPath = path;
                }
            }
            return newAccess(basePath, canonicalBasePath, baseGitUrl, resolver, req);
        }

        protected DefaultAccess newAccess(File basePath, String canonicalBasePath, String baseGitUrl,
                FileResolver<HttpServletRequest> resolver, HttpServletRequest req) {
            return new DefaultAccess(basePath, canonicalBasePath, baseGitUrl, resolver, req);
        }
    }

    protected final File basePath;
    protected final String canonicalBasePath;
    protected final String baseGitUrl;
    protected final FileResolver<HttpServletRequest> resolver;
    protected final HttpServletRequest req;

    protected DefaultAccess(File basePath, String canonicalBasePath, String baseGitUrl,
            FileResolver<HttpServletRequest> resolver, HttpServletRequest req) {
        this.basePath = checkNotNull(basePath, "basePath");
        this.canonicalBasePath = checkNotNull(canonicalBasePath, "canonicalBasePath");
        this.baseGitUrl = checkNotNull(baseGitUrl, "baseGitUrl");
        this.resolver = checkNotNull(resolver, "resolver");
        this.req = checkNotNull(req, "req");
    }

    @Override
    public Map<String, RepositoryDescription> listRepositories(Set<String> branches) throws IOException {
        Map<String, RepositoryDescription> repos = Maps.newTreeMap();
        for (Repository repo : scanRepositories(basePath, req)) {
            repos.put(getRepositoryName(repo), buildDescription(repo, branches));
            repo.close();
        }
        return repos;
    }

    @Override
    public Object getUserKey() {
        // Always return the same anonymous user key (effectively running with the
        // same user permissions as the JVM). Subclasses may override this behavior.
        return ANONYMOUS_USER_KEY;
    }

    @Override
    public String getRepositoryName() {
        return getRepositoryName(ServletUtils.getRepository(req));
    }

    @Override
    public RepositoryDescription getRepositoryDescription() throws IOException {
        return buildDescription(ServletUtils.getRepository(req), Collections.<String>emptySet());
    }

    private String getRepositoryName(Repository repo) {
        String path = getRelativePath(repo);
        if (repo.isBare() && path.endsWith(".git")) {
            path = path.substring(0, path.length() - 4);
        }
        return path;
    }

    private String getRelativePath(Repository repo) {
        String path = repo.isBare() ? repo.getDirectory().getPath() : repo.getDirectory().getParent();
        if (repo.isBare()) {
            path = repo.getDirectory().getPath();
            if (path.endsWith(".git")) {
                path = path.substring(0, path.length() - 4);
            }
        } else {
            path = repo.getDirectory().getParent();
        }
        return getRelativePath(path);
    }

    private String getRelativePath(String path) {
        String base = basePath.getPath();
        if (path.startsWith(base)) {
            return path.substring(base.length() + 1);
        }
        if (path.startsWith(canonicalBasePath)) {
            return path.substring(canonicalBasePath.length() + 1);
        }
        throw new IllegalStateException(String.format("Repository path %s is outside base path %s", path, base));
    }

    private String loadDescriptionText(Repository repo) throws IOException {
        String desc = null;
        StoredConfig config = repo.getConfig();
        IOException configError = null;
        try {
            config.load();
            desc = config.getString("gitweb", null, "description");
        } catch (ConfigInvalidException e) {
            configError = new IOException(e);
        }
        if (desc == null) {
            File descFile = new File(repo.getDirectory(), "description");
            if (descFile.exists()) {
                desc = new String(IO.readFully(descFile));
            } else if (configError != null) {
                throw configError;
            }
        }
        return desc;
    }

    private RepositoryDescription buildDescription(Repository repo, Set<String> branches) throws IOException {
        RepositoryDescription desc = new RepositoryDescription();
        desc.name = getRepositoryName(repo);
        desc.cloneUrl = baseGitUrl + getRelativePath(repo);
        desc.description = loadDescriptionText(repo);
        if (!branches.isEmpty()) {
            desc.branches = Maps.newLinkedHashMap();
            for (String name : branches) {
                Ref ref = repo.getRef(normalizeRefName(name));
                if ((ref != null) && (ref.getObjectId() != null)) {
                    desc.branches.put(name, ref.getObjectId().name());
                }
            }
        }
        return desc;
    }

    private static String normalizeRefName(String name) {
        if (name.startsWith("refs/")) {
            return name;
        }
        return "refs/heads/" + name;
    }

    private Collection<Repository> scanRepositories(final File basePath, final HttpServletRequest req)
            throws IOException {
        List<Repository> repos = Lists.newArrayList();
        Queue<File> todo = Queues.newArrayDeque();
        File[] baseFiles = basePath.listFiles();
        if (baseFiles == null) {
            throw new IOException("base path is not a directory: " + basePath.getPath());
        }
        todo.addAll(Arrays.asList(baseFiles));
        while (!todo.isEmpty()) {
            File file = todo.remove();
            try {
                repos.add(resolver.open(req, getRelativePath(file.getPath())));
            } catch (RepositoryNotFoundException e) {
                File[] children = file.listFiles();
                if (children != null) {
                    todo.addAll(Arrays.asList(children));
                }
            } catch (ServiceNotEnabledException e) {
                throw new IOException(e);
            }
        }
        return repos;
    }
}