Allow plugins to contribute Prolog predicates

A new extension point is defined that allows plugins to provide
additional packages that contain Prolog predicates. The predicates
can e.g. be used in the project submit rules.

Change-Id: I097a9671d6e740755252fb494e75e44dfd3c3d49
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2012-08-21 10:29:57 +02:00
parent 79db583f2e
commit 5c62e38602
4 changed files with 120 additions and 8 deletions

View File

@@ -0,0 +1,65 @@
// Copyright (C) 2012 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 com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.gerrit.extensions.registration.DynamicSet;
import java.util.Collection;
/**
* Loads the classes for Prolog predicates.
*/
public class PredicateClassLoader extends ClassLoader {
private final Multimap<String, ClassLoader> packageClassLoaderMap =
LinkedHashMultimap.create();
public PredicateClassLoader(
final DynamicSet<PredicateProvider> predicateProviders,
final ClassLoader parent) {
super(parent);
for (PredicateProvider predicateProvider : predicateProviders) {
for (String pkg : predicateProvider.getPackages()) {
packageClassLoaderMap.put(pkg, predicateProvider.getClass()
.getClassLoader());
}
}
}
@Override
protected Class<?> findClass(final String className)
throws ClassNotFoundException {
final Collection<ClassLoader> classLoaders =
packageClassLoaderMap.get(getPackageName(className));
for (final ClassLoader cl : classLoaders) {
try {
return Class.forName(className, true, cl);
} catch (ClassNotFoundException e) {
// ignore
}
}
throw new ClassNotFoundException(className);
}
private static String getPackageName(String className) {
final int pos = className.lastIndexOf('.');
if (pos < 0) {
return "";
}
return className.substring(0, pos);
}
}

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2012 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 com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.googlecode.prolog_cafe.lang.Predicate;
/**
* Provides additional packages that contain Prolog predicates that should be
* made available in the Prolog environment. The predicates can e.g. be used in
* the project submit rules.
*
* Each Java class defining a Prolog predicate must be in one of the provided
* packages and its name must apply to the 'PRED_[functor]_[arity]' format. In
* addition it must extend {@link Predicate}.
*/
@ExtensionPoint
public interface PredicateProvider {
/** Return set of packages that contain Prolog predicates */
public ImmutableSet<String> getPackages();
}

View File

@@ -14,11 +14,13 @@
package com.google.gerrit.rules;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.config.FactoryModule;
public class PrologModule extends FactoryModule {
@Override
protected void configure() {
DynamicSet.setOf(binder(), PredicateProvider.class);
factory(PrologEnvironment.Factory.class);
}
}

View File

@@ -16,6 +16,9 @@ package com.google.gerrit.rules;
import static com.googlecode.prolog_cafe.lang.PrologMachineCopy.save;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -52,6 +55,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -69,10 +73,8 @@ public class RulesCache {
/** Default size of the internal Prolog database within each interpreter. */
private static final int DB_MAX = 256;
private static final String[] PACKAGE_LIST = {
Prolog.BUILTIN,
"gerrit",
};
private static final List<String> PACKAGE_LIST = ImmutableList.of(
Prolog.BUILTIN, "gerrit");
private final Map<ObjectId, MachineRef> machineCache =
new HashMap<ObjectId, MachineRef>();
@@ -94,16 +96,18 @@ public class RulesCache {
private final File cacheDir;
private final File rulesDir;
private final GitRepositoryManager gitMgr;
private final DynamicSet<PredicateProvider> predicateProviders;
private final ClassLoader systemLoader;
private final PrologMachineCopy defaultMachine;
@Inject
protected RulesCache(@GerritServerConfig Config config, SitePaths site,
GitRepositoryManager gm) {
GitRepositoryManager gm, DynamicSet<PredicateProvider> predicateProviders) {
enableProjectRules = config.getBoolean("rules", null, "enable", true);
cacheDir = site.resolve(config.getString("cache", null, "directory"));
rulesDir = cacheDir != null ? new File(cacheDir, "rules") : null;
gitMgr = gm;
this.predicateProviders = predicateProviders;
systemLoader = getClass().getClassLoader();
defaultMachine = save(newEmptyMachine(systemLoader));
@@ -207,15 +211,22 @@ public class RulesCache {
}
}
private static BufferingPrologControl newEmptyMachine(ClassLoader cl) {
private BufferingPrologControl newEmptyMachine(ClassLoader cl) {
BufferingPrologControl ctl = new BufferingPrologControl();
ctl.setMaxArity(PrologEnvironment.MAX_ARITY);
ctl.setMaxDatabaseSize(DB_MAX);
ctl.setPrologClassLoader(new PrologClassLoader(cl));
ctl.setPrologClassLoader(new PrologClassLoader(new PredicateClassLoader(
predicateProviders, cl)));
ctl.setEnabled(EnumSet.allOf(Prolog.Feature.class), false);
List<String> packages = Lists.newArrayList();
packages.addAll(PACKAGE_LIST);
for (PredicateProvider predicateProvider : predicateProviders) {
packages.addAll(predicateProvider.getPackages());
}
// Bootstrap the interpreter and ensure there is clean state.
ctl.initialize(PACKAGE_LIST);
ctl.initialize(packages.toArray(new String[packages.size()]));
return ctl;
}