summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java293
1 files changed, 0 insertions, 293 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java
deleted file mode 100644
index 9ab0dd65ce..0000000000
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright (C) 2011 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.rules;
-
-import static com.googlecode.prolog_cafe.lang.PrologMachineCopy.save;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.googlecode.prolog_cafe.exceptions.CompileException;
-import com.googlecode.prolog_cafe.exceptions.SyntaxException;
-import com.googlecode.prolog_cafe.exceptions.TermException;
-import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
-import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
-import com.googlecode.prolog_cafe.lang.ListTerm;
-import com.googlecode.prolog_cafe.lang.Prolog;
-import com.googlecode.prolog_cafe.lang.PrologClassLoader;
-import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
-import com.googlecode.prolog_cafe.lang.StructureTerm;
-import com.googlecode.prolog_cafe.lang.SymbolTerm;
-import com.googlecode.prolog_cafe.lang.Term;
-import java.io.IOException;
-import java.io.PushbackReader;
-import java.io.Reader;
-import java.io.StringReader;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.eclipse.jgit.errors.LargeObjectException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * Manages a cache of compiled Prolog rules.
- *
- * <p>Rules are loaded from the {@code site_path/cache/rules/rules-SHA1.jar}, where {@code SHA1} is
- * the SHA1 of the Prolog {@code rules.pl} in a project's {@link RefNames#REFS_CONFIG} branch.
- */
-@Singleton
-public class RulesCache {
- private static final ImmutableList<String> PACKAGE_LIST =
- ImmutableList.of(Prolog.BUILTIN, "gerrit");
-
- private static final class MachineRef extends WeakReference<PrologMachineCopy> {
- final ObjectId key;
-
- MachineRef(ObjectId key, PrologMachineCopy pcm, ReferenceQueue<PrologMachineCopy> queue) {
- super(pcm, queue);
- this.key = key;
- }
- }
-
- private final boolean enableProjectRules;
- private final int maxDbSize;
- private final int maxSrcBytes;
- private final Path cacheDir;
- private final Path rulesDir;
- private final GitRepositoryManager gitMgr;
- private final DynamicSet<PredicateProvider> predicateProviders;
- private final ClassLoader systemLoader;
- private final PrologMachineCopy defaultMachine;
- private final Map<ObjectId, MachineRef> machineCache = new HashMap<>();
- private final ReferenceQueue<PrologMachineCopy> dead = new ReferenceQueue<>();
-
- @Inject
- protected RulesCache(
- @GerritServerConfig Config config,
- SitePaths site,
- GitRepositoryManager gm,
- DynamicSet<PredicateProvider> predicateProviders) {
- maxDbSize = config.getInt("rules", null, "maxPrologDatabaseSize", 256);
- maxSrcBytes = config.getInt("rules", null, "maxSourceBytes", 128 << 10);
- enableProjectRules = config.getBoolean("rules", null, "enable", true) && maxSrcBytes > 0;
- cacheDir = site.resolve(config.getString("cache", null, "directory"));
- rulesDir = cacheDir != null ? cacheDir.resolve("rules") : null;
- gitMgr = gm;
- this.predicateProviders = predicateProviders;
-
- systemLoader = getClass().getClassLoader();
- defaultMachine = save(newEmptyMachine(systemLoader));
- }
-
- public boolean isProjectRulesEnabled() {
- return enableProjectRules;
- }
-
- /**
- * Locate a cached Prolog machine state, or create one if not available.
- *
- * @return a Prolog machine, after loading the specified rules.
- * @throws CompileException the machine cannot be created.
- */
- public synchronized PrologMachineCopy loadMachine(Project.NameKey project, ObjectId rulesId)
- throws CompileException {
- if (!enableProjectRules || project == null || rulesId == null) {
- return defaultMachine;
- }
-
- Reference<? extends PrologMachineCopy> ref = machineCache.get(rulesId);
- if (ref != null) {
- PrologMachineCopy pmc = ref.get();
- if (pmc != null) {
- return pmc;
- }
-
- machineCache.remove(rulesId);
- ref.enqueue();
- }
-
- gc();
-
- PrologMachineCopy pcm = createMachine(project, rulesId);
- MachineRef newRef = new MachineRef(rulesId, pcm, dead);
- machineCache.put(rulesId, newRef);
- return pcm;
- }
-
- public PrologMachineCopy loadMachine(String name, Reader in) throws CompileException {
- PrologMachineCopy pmc = consultRules(name, in);
- if (pmc == null) {
- throw new CompileException("Cannot consult rules from the stream " + name);
- }
- return pmc;
- }
-
- private void gc() {
- Reference<?> ref;
- while ((ref = dead.poll()) != null) {
- ObjectId key = ((MachineRef) ref).key;
- if (machineCache.get(key) == ref) {
- machineCache.remove(key);
- }
- }
- }
-
- private PrologMachineCopy createMachine(Project.NameKey project, ObjectId rulesId)
- throws CompileException {
- // If the rules are available as a complied JAR on local disk, prefer
- // that over dynamic consult as the bytecode will be faster.
- //
- if (rulesDir != null) {
- Path jarPath = rulesDir.resolve("rules-" + rulesId.getName() + ".jar");
- if (Files.isRegularFile(jarPath)) {
- URL[] cp = new URL[] {toURL(jarPath)};
- return save(newEmptyMachine(new URLClassLoader(cp, systemLoader)));
- }
- }
-
- // Dynamically consult the rules into the machine's internal database.
- //
- String rules = read(project, rulesId);
- PrologMachineCopy pmc = consultRules("rules.pl", new StringReader(rules));
- if (pmc == null) {
- throw new CompileException("Cannot consult rules of " + project);
- }
- return pmc;
- }
-
- private PrologMachineCopy consultRules(String name, Reader rules) throws CompileException {
- BufferingPrologControl ctl = newEmptyMachine(systemLoader);
- PushbackReader in = new PushbackReader(rules, Prolog.PUSHBACK_SIZE);
- try {
- if (!ctl.execute(
- Prolog.BUILTIN, "consult_stream", SymbolTerm.intern(name), new JavaObjectTerm(in))) {
- return null;
- }
- } catch (SyntaxException e) {
- throw new CompileException(e.toString(), e);
- } catch (TermException e) {
- Term m = e.getMessageTerm();
- if (m instanceof StructureTerm && "syntax_error".equals(m.name()) && m.arity() >= 1) {
- StringBuilder msg = new StringBuilder();
- if (m.arg(0) instanceof ListTerm) {
- msg.append(Joiner.on(' ').join(((ListTerm) m.arg(0)).toJava()));
- } else {
- msg.append(m.arg(0).toString());
- }
- if (m.arity() == 2 && m.arg(1) instanceof StructureTerm && "at".equals(m.arg(1).name())) {
- Term at = m.arg(1).arg(0).dereference();
- if (at instanceof ListTerm) {
- msg.append(" at: ");
- msg.append(prettyProlog(at));
- }
- }
- throw new CompileException(msg.toString(), e);
- }
- throw new CompileException("Error while consulting rules from " + name, e);
- } catch (RuntimeException e) {
- throw new CompileException("Error while consulting rules from " + name, e);
- }
- return save(ctl);
- }
-
- private static String prettyProlog(Term at) {
- StringBuilder b = new StringBuilder();
- for (Object o : ((ListTerm) at).toJava()) {
- if (o instanceof Term) {
- Term t = (Term) o;
- if (!(t instanceof StructureTerm)) {
- b.append(t.toString()).append(' ');
- continue;
- }
- switch (t.name()) {
- case "atom":
- SymbolTerm atom = (SymbolTerm) t.arg(0);
- b.append(atom.toString());
- break;
- case "var":
- b.append(t.arg(0).toString());
- break;
- }
- } else {
- b.append(o);
- }
- }
- return b.toString().trim();
- }
-
- private String read(Project.NameKey project, ObjectId rulesId) throws CompileException {
- try (Repository git = gitMgr.openRepository(project)) {
- try {
- ObjectLoader ldr = git.open(rulesId, Constants.OBJ_BLOB);
- byte[] raw = ldr.getCachedBytes(maxSrcBytes);
- return RawParseUtils.decode(raw);
- } catch (LargeObjectException e) {
- throw new CompileException("rules of " + project + " are too large", e);
- } catch (RuntimeException | IOException e) {
- throw new CompileException("Cannot load rules of " + project, e);
- }
- } catch (IOException e) {
- throw new CompileException("Cannot open repository " + project, e);
- }
- }
-
- private BufferingPrologControl newEmptyMachine(ClassLoader cl) {
- BufferingPrologControl ctl = new BufferingPrologControl();
- ctl.setMaxDatabaseSize(maxDbSize);
- ctl.setPrologClassLoader(
- new PrologClassLoader(new PredicateClassLoader(predicateProviders, cl)));
- ctl.setEnabled(EnumSet.allOf(Prolog.Feature.class), false);
-
- List<String> packages = new ArrayList<>();
- packages.addAll(PACKAGE_LIST);
- for (PredicateProvider predicateProvider : predicateProviders) {
- packages.addAll(predicateProvider.getPackages());
- }
-
- // Bootstrap the interpreter and ensure there is clean state.
- ctl.initialize(packages.toArray(new String[packages.size()]));
- return ctl;
- }
-
- private static URL toURL(Path jarPath) throws CompileException {
- try {
- return jarPath.toUri().toURL();
- } catch (MalformedURLException e) {
- throw new CompileException("Cannot create URL for " + jarPath, e);
- }
- }
-}