Load precompiled prolog rules from jar file

Looks in (site)/cache/rules for a jar file called:
rules-(sha1 of rules.pl).jar
Loads the precompiled prolog rules and uses them instead of
consulting rules.pl. If the jar does not exist, consults rules.pl.
If rules.pl does not exist, uses the default submit rules.

Change-Id: Iae415de08dbe249eff26b753d258fd7f79a8771f
This commit is contained in:
Jason Tsay
2011-06-14 15:19:26 -07:00
committed by Shawn O. Pearce
parent 76409cf7fa
commit 4efeb4fc64
6 changed files with 160 additions and 5 deletions

View File

@@ -76,6 +76,7 @@ public class ProjectConfig extends VersionedMetaData {
private Map<String, AccessSection> accessSections;
private List<ValidationError> validationErrors;
private String prologRules;
private ObjectId rulesId;
public static ProjectConfig read(MetaDataUpdate update) throws IOException,
ConfigInvalidException {
@@ -163,6 +164,14 @@ public class ProjectConfig extends VersionedMetaData {
return prologRules;
}
/**
* @return the project's rules.pl ObjectId, if present in the branch.
* Null if it doesn't exist.
*/
public ObjectId getRulesId() {
return rulesId;
}
/**
* Check all GroupReferences use current group name, repairing stale ones.
*
@@ -204,6 +213,7 @@ public class ProjectConfig extends VersionedMetaData {
Map<String, GroupReference> groupsByName = readGroupList();
prologRules = readUTF8("rules.pl");
rulesId = getObjectId("rules.pl");
Config rc = readConfig(PROJECT_CONFIG);
project = new Project(projectName);

View File

@@ -264,6 +264,19 @@ public abstract class VersionedMetaData {
}
}
protected ObjectId getObjectId(String fileName) throws IOException {
if (revision == null) {
return null;
}
TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree());
if (tw != null) {
return tw.getObjectId(0);
}
return null;
}
protected static void set(Config rc, String section, String subsection,
String name, String value) {
if (value != null) {

View File

@@ -60,6 +60,7 @@ public class ProjectState {
private final ProjectConfig config;
private final Set<AccountGroup.UUID> localOwners;
private final ClassLoader ruleLoader;
/** Last system time the configuration's revision was examined. */
private transient long lastCheckTime;
@@ -71,6 +72,7 @@ public class ProjectState {
final ProjectControl.AssistedFactory projectControlFactory,
final PrologEnvironment.Factory envFactory,
final GitRepositoryManager gitMgr,
final RulesCache rulesCache,
@Assisted final ProjectConfig config) {
this.projectCache = projectCache;
this.isAllProjects = config.getProject().getNameKey().equals(allProjectsName);
@@ -79,6 +81,11 @@ public class ProjectState {
this.gitMgr = gitMgr;
this.config = config;
this.lastCheckTime = System.currentTimeMillis();
if (rulesCache != null) {
ruleLoader = rulesCache.getClassLoader(config.getRulesId());
} else {
ruleLoader = null;
}
HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>();
AccessSection all = config.getAccessSection(AccessSection.ALL);
@@ -126,7 +133,10 @@ public class ProjectState {
/** @return Construct a new PrologEnvironment for the calling thread. */
public PrologEnvironment newPrologEnvironment() throws CompileException {
// TODO Replace this with a per-project ClassLoader to isolate rules.
if (ruleLoader != null) {
return envFactory.create(ruleLoader);
}
PrologEnvironment env = envFactory.create(getClass().getClassLoader());
//consult rules.pl at refs/meta/config branch for custom submit rules

View File

@@ -0,0 +1,121 @@
// 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.server.project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
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.util.HashMap;
import java.util.Map;
@Singleton
public class RulesCache {
private static final Logger log = LoggerFactory.getLogger(RulesCache.class);
private final Map<ObjectId, LoaderRef> classLoaderCache =
new HashMap<ObjectId, LoaderRef>();
private final ReferenceQueue<ClassLoader> DEAD = new ReferenceQueue<ClassLoader>();
private final File cacheDir;
private final File rulesDir;
private final class LoaderRef extends WeakReference<ClassLoader> {
final ObjectId key;
LoaderRef(ObjectId key, ClassLoader loader) {
super(loader, DEAD);
this.key = key;
}
}
@Inject
protected RulesCache (@GerritServerConfig Config config, SitePaths site) {
cacheDir = site.resolve(config.getString("cache", null, "directory"));
if (cacheDir != null) {
rulesDir = new File(cacheDir, "rules");
} else {
rulesDir = null;
}
}
/** @return URLClassLoader with precompiled rules jar from rules.pl if it exists,
* null otherwise
*/
public synchronized ClassLoader getClassLoader(ObjectId rulesId) {
if (rulesId == null || rulesDir == null) {
return null;
}
Reference<? extends ClassLoader> ref = classLoaderCache.get(rulesId);
if (ref != null) {
ClassLoader cl = ref.get();
if (cl != null) {
return cl;
}
classLoaderCache.remove(rulesId);
ref.enqueue();
}
cleanCache();
//read jar from (site)/cache/rules
//the included jar file should be in format:
//rules-(rules.pl's sha1).jar
File jarFile = new File(rulesDir, "rules-" + rulesId.getName() + ".jar");
if (!jarFile.isFile()) {
return null;
}
ClassLoader defaultLoader = getClass().getClassLoader();
URL url;
try {
url = jarFile.toURI().toURL();
} catch (MalformedURLException e) {
log.error("Path to rules jar is broken", e);
return null;
}
ClassLoader urlLoader = new URLClassLoader(new URL[]{url}, defaultLoader);
LoaderRef lRef = new LoaderRef(rulesId, urlLoader);
classLoaderCache.put(rulesId, lRef);
return urlLoader;
}
private void cleanCache() {
Reference<? extends ClassLoader> ref;
while ((ref = DEAD.poll()) != null) {
ObjectId key = ((LoaderRef) ref).key;
if (classLoaderCache.get(key) == ref) {
classLoaderCache.remove(key);
}
}
}
}

View File

@@ -170,12 +170,12 @@ is_all_ok(_) :- fail.
%%
locate_submit_rule(RuleName) :-
clause(user:submit_rule(_), _),
'$compiled_predicate'(user, submit_rule, 1),
!,
RuleName = user:submit_rule
.
locate_submit_rule(RuleName) :-
'$compiled_predicate'(user, submit_rule, 1),
clause(user:submit_rule(_), _),
!,
RuleName = user:submit_rule
.

View File

@@ -317,12 +317,13 @@ public class RefControlTest extends TestCase {
PrologEnvironment.Factory envFactory = null;
GitRepositoryManager mgr = null;
ProjectControl.AssistedFactory projectControlFactory = null;
RulesCache rulesCache = null;
all.put(local.getProject().getNameKey(), new ProjectState(
projectCache, allProjectsName, projectControlFactory,
envFactory, mgr, local));
envFactory, mgr, rulesCache, local));
all.put(parent.getProject().getNameKey(), new ProjectState(
projectCache, allProjectsName, projectControlFactory,
envFactory, mgr, parent));
envFactory, mgr, rulesCache, parent));
return all.get(local.getProject().getNameKey());
}