com.google.gerrit.server.project.ProjectCacheImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.server.project.ProjectCacheImpl.java

Source

// Copyright (C) 2008 The Android Open Source Project
//
// 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.gerrit.server.project;

import com.google.common.base.Throwables;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;

import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** Cache of project information, including access rights. */
@Singleton
public class ProjectCacheImpl implements ProjectCache {
    private static final Logger log = LoggerFactory.getLogger(ProjectCacheImpl.class);

    private static final String CACHE_NAME = "projects";
    private static final String CACHE_LIST = "project_list";

    public static Module module() {
        return new CacheModule() {
            @Override
            protected void configure() {
                cache(CACHE_NAME, String.class, ProjectState.class).loader(Loader.class);

                cache(CACHE_LIST, ListKey.class, new TypeLiteral<SortedSet<Project.NameKey>>() {
                }).maximumWeight(1).loader(Lister.class);

                bind(ProjectCacheImpl.class);
                bind(ProjectCache.class).to(ProjectCacheImpl.class);
            }
        };
    }

    private final AllProjectsName allProjectsName;
    private final LoadingCache<String, ProjectState> byName;
    private final LoadingCache<ListKey, SortedSet<Project.NameKey>> list;
    private final Lock listLock;
    private final ProjectCacheClock clock;

    @Inject
    ProjectCacheImpl(final AllProjectsName allProjectsName,
            @Named(CACHE_NAME) LoadingCache<String, ProjectState> byName,
            @Named(CACHE_LIST) LoadingCache<ListKey, SortedSet<Project.NameKey>> list, ProjectCacheClock clock) {
        this.allProjectsName = allProjectsName;
        this.byName = byName;
        this.list = list;
        this.listLock = new ReentrantLock(true /* fair */);
        this.clock = clock;
    }

    @Override
    public ProjectState getAllProjects() {
        ProjectState state = get(allProjectsName);
        if (state == null) {
            // This should never occur, the server must have this
            // project to process anything.
            throw new IllegalStateException("Missing project " + allProjectsName);
        }
        return state;
    }

    @Override
    public ProjectState get(final Project.NameKey projectName) {
        try {
            return checkedGet(projectName);
        } catch (IOException e) {
            return null;
        }
    }

    @Override
    public ProjectState checkedGet(Project.NameKey projectName) throws IOException {
        if (projectName == null) {
            return null;
        }
        try {
            ProjectState state = byName.get(projectName.get());
            if (state != null && state.needsRefresh(clock.read())) {
                byName.invalidate(projectName.get());
                state = byName.get(projectName.get());
            }
            return state;
        } catch (ExecutionException e) {
            if (!(e.getCause() instanceof RepositoryNotFoundException)) {
                log.warn(String.format("Cannot read project %s", projectName.get()), e);
                Throwables.propagateIfInstanceOf(e.getCause(), IOException.class);
                throw new IOException(e);
            }
            return null;
        }
    }

    @Override
    public void evict(final Project p) {
        if (p != null) {
            byName.invalidate(p.getNameKey().get());
        }
    }

    /** Invalidate the cached information about the given project. */
    public void evict(final Project.NameKey p) {
        if (p != null) {
            byName.invalidate(p.get());
        }
    }

    @Override
    public void remove(final Project p) {
        listLock.lock();
        try {
            SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
            n.remove(p.getNameKey());
            list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
        } catch (ExecutionException e) {
            log.warn("Cannot list avaliable projects", e);
        } finally {
            listLock.unlock();
        }
        evict(p);
    }

    @Override
    public void onCreateProject(Project.NameKey newProjectName) {
        listLock.lock();
        try {
            SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
            n.add(newProjectName);
            list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
        } catch (ExecutionException e) {
            log.warn("Cannot list avaliable projects", e);
        } finally {
            listLock.unlock();
        }
    }

    @Override
    public Iterable<Project.NameKey> all() {
        try {
            return list.get(ListKey.ALL);
        } catch (ExecutionException e) {
            log.warn("Cannot list available projects", e);
            return Collections.emptyList();
        }
    }

    @Override
    public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
        Set<AccountGroup.UUID> groups = Sets.newHashSet();
        for (Project.NameKey n : all()) {
            ProjectState p = byName.getIfPresent(n.get());
            if (p != null) {
                groups.addAll(p.getConfig().getAllGroupUUIDs());
            }
        }
        return groups;
    }

    @Override
    public Iterable<Project.NameKey> byName(final String pfx) {
        final Iterable<Project.NameKey> src;
        try {
            src = list.get(ListKey.ALL).tailSet(new Project.NameKey(pfx));
        } catch (ExecutionException e) {
            return Collections.emptyList();
        }
        return new Iterable<Project.NameKey>() {
            @Override
            public Iterator<Project.NameKey> iterator() {
                return new Iterator<Project.NameKey>() {
                    private Iterator<Project.NameKey> itr = src.iterator();
                    private Project.NameKey next;

                    @Override
                    public boolean hasNext() {
                        if (next != null) {
                            return true;
                        }

                        if (!itr.hasNext()) {
                            return false;
                        }

                        Project.NameKey r = itr.next();
                        if (r.get().startsWith(pfx)) {
                            next = r;
                            return true;
                        } else {
                            itr = Collections.<Project.NameKey>emptyList().iterator();
                            return false;
                        }
                    }

                    @Override
                    public Project.NameKey next() {
                        if (!hasNext()) {
                            throw new NoSuchElementException();
                        }

                        Project.NameKey r = next;
                        next = null;
                        return r;
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    static class Loader extends CacheLoader<String, ProjectState> {
        private final ProjectState.Factory projectStateFactory;
        private final GitRepositoryManager mgr;

        @Inject
        Loader(ProjectState.Factory psf, GitRepositoryManager g) {
            projectStateFactory = psf;
            mgr = g;
        }

        @Override
        public ProjectState load(String projectName) throws Exception {
            Project.NameKey key = new Project.NameKey(projectName);
            Repository git = mgr.openRepository(key);
            try {
                ProjectConfig cfg = new ProjectConfig(key);
                cfg.load(git);
                return projectStateFactory.create(cfg);
            } finally {
                git.close();
            }
        }
    }

    static class ListKey {
        static final ListKey ALL = new ListKey();

        private ListKey() {
        }
    }

    static class Lister extends CacheLoader<ListKey, SortedSet<Project.NameKey>> {
        private final GitRepositoryManager mgr;

        @Inject
        Lister(GitRepositoryManager mgr) {
            this.mgr = mgr;
        }

        @Override
        public SortedSet<Project.NameKey> load(ListKey key) throws Exception {
            return mgr.list();
        }
    }
}