diff options
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.java | 293 |
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); - } - } -} |