Add resource closing to PrologEnvironment
Adds close() to PrologEnvironment that runs all the necessary routines
to close any resources when the environment is no longer needed.
When computing a resource to store that needs to be closed later, add
a cleanup routine using addToCleanup(). Adds the Repository
stored value as an example. Refactors ChangeControl to have the
environments clean up after themselves.
Change-Id: I5d109be40cb9212068624820999d522e71ef6fd6
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
index 744feb3..5d86954 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
@@ -22,8 +22,14 @@
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
/**
@@ -34,6 +40,10 @@
* A single copy of the Prolog interpreter, for the current thread.
*/
public class PrologEnvironment extends BufferingPrologControl {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(PrologEnvironment.class);
+
static final int MAX_ARITY = 8;
public static interface Factory {
@@ -48,6 +58,7 @@
private final Injector injector;
private final Map<StoredValue<Object>, Object> storedValues;
+ private List<Runnable> cleanup;
@Inject
PrologEnvironment(Injector i, @Assisted PrologMachineCopy src) {
@@ -56,6 +67,7 @@
setMaxArity(MAX_ARITY);
setEnabled(EnumSet.allOf(Prolog.Feature.class), false);
storedValues = new HashMap<StoredValue<Object>, Object>();
+ cleanup = new LinkedList<Runnable>();
}
/** Get the global Guice Injector that configured the environment. */
@@ -87,8 +99,42 @@
/**
* Copy the stored values from another interpreter to this one.
+ * Also gets the cleanup from the child interpreter
*/
public void copyStoredValues(PrologEnvironment child) {
storedValues.putAll(child.storedValues);
+ setCleanup(child.cleanup);
+ }
+
+ /**
+ * Assign the environment a cleanup list (in order to use a centralized list)
+ * If this enivronment's list is non-empty, append its cleanup tasks to the
+ * assigning list.
+ */
+ public void setCleanup(List<Runnable> newCleanupList) {
+ newCleanupList.addAll(cleanup);
+ cleanup = newCleanupList;
+ }
+
+ /**
+ * Adds cleanup task to run when close() is called
+ * @param task is run when close() is called
+ */
+ public void addToCleanup(Runnable task) {
+ cleanup.add(task);
+ }
+
+ /**
+ * Release resources stored in interpreter's hash manager.
+ */
+ public void close() {
+ for (final Iterator<Runnable> i = cleanup.iterator(); i.hasNext();) {
+ try {
+ i.next().run();
+ } catch (Throwable err) {
+ log.error("Failed to execute cleanup for PrologEnvironment", err);
+ }
+ i.remove();
+ }
}
}
\ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 895c70b..cdcbb41 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -22,6 +22,7 @@
import com.google.gerrit.reviewdb.PatchSetInfo;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListKey;
@@ -32,7 +33,9 @@
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.SystemException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
public final class StoredValues {
public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class);
@@ -75,6 +78,30 @@
}
};
+ public static final StoredValue<Repository> REPOSITORY = new StoredValue<Repository>() {
+ @Override
+ public Repository createValue(Prolog engine) {
+ PrologEnvironment env = (PrologEnvironment) engine.control;
+ GitRepositoryManager gitMgr =
+ env.getInjector().getInstance(GitRepositoryManager.class);
+ Change change = StoredValues.CHANGE.get(engine);
+ Project.NameKey projectKey = change.getProject();
+ final Repository repo;
+ try {
+ repo = gitMgr.openRepository(projectKey);
+ } catch (RepositoryNotFoundException e) {
+ throw new SystemException(e.getMessage());
+ }
+ env.addToCleanup(new Runnable() {
+ @Override
+ public void run() {
+ repo.close();
+ }
+ });
+ return repo;
+ }
+ };
+
private StoredValues() {
}
}
\ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 2c55462..6d44511 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -243,8 +244,11 @@
return ruleError("Patch set " + patchSetId + " is not current");
}
+ List<Term> results = new ArrayList<Term>();
+ Term submitRule;
ProjectState projectState = getProjectControl().getProjectState();
PrologEnvironment env;
+
try {
env = projectState.newPrologEnvironment();
} catch (CompileException err) {
@@ -252,76 +256,80 @@
+ getProject().getName(), err);
}
- env.set(StoredValues.REVIEW_DB, db);
- env.set(StoredValues.CHANGE, change);
- env.set(StoredValues.PATCH_SET_ID, patchSetId);
- env.set(StoredValues.CHANGE_CONTROL, this);
-
- Term submitRule = env.once(
- "gerrit", "locate_submit_rule",
- new VariableTerm());
- if (submitRule == null) {
- return logRuleError("No user:submit_rule found for "
- + getProject().getName());
- }
-
- List<Term> results = new ArrayList<Term>();
try {
- for (Term[] template : env.all(
- "gerrit", "can_submit",
- submitRule,
- new VariableTerm())) {
- results.add(template[1]);
- }
- } catch (PrologException err) {
- return logRuleError("Exception calling " + submitRule + " on change "
- + change.getId() + " of " + getProject().getName(), err);
- } catch (RuntimeException err) {
- return logRuleError("Exception calling " + submitRule + " on change "
- + change.getId() + " of " + getProject().getName(), err);
- }
+ env.set(StoredValues.REVIEW_DB, db);
+ env.set(StoredValues.CHANGE, change);
+ env.set(StoredValues.PATCH_SET_ID, patchSetId);
+ env.set(StoredValues.CHANGE_CONTROL, this);
- ProjectState parentState = projectState.getParentState();
- PrologEnvironment childEnv = env;
- Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
- projectsSeen.add(getProject().getNameKey());
-
- while (parentState != null) {
- if (!projectsSeen.add(parentState.getProject().getNameKey())) {
- //parent has been seen before, stop walk up inheritance tree
- break;
+ submitRule = env.once(
+ "gerrit", "locate_submit_rule",
+ new VariableTerm());
+ if (submitRule == null) {
+ return logRuleError("No user:submit_rule found for "
+ + getProject().getName());
}
- PrologEnvironment parentEnv;
+
try {
- parentEnv = parentState.newPrologEnvironment();
- } catch (CompileException err) {
- return logRuleError("Cannot consult rules.pl for "
- + parentState.getProject().getName(), err);
- }
- parentEnv.copyStoredValues(childEnv);
- Term filterRule =
- parentEnv.once("gerrit", "locate_submit_filter", new VariableTerm());
- if (filterRule != null) {
- try {
- Term resultsTerm = toListTerm(results);
- results.clear();
- Term[] template = parentEnv.once(
- "gerrit", "filter_submit_results",
- filterRule,
- resultsTerm,
- new VariableTerm());
- results.addAll(((ListTerm) template[2]).toJava());
- } catch (PrologException err) {
- return logRuleError("Exception calling " + filterRule + " on change "
- + change.getId() + " of " + parentState.getProject().getName(), err);
- } catch (RuntimeException err) {
- return logRuleError("Exception calling " + filterRule + " on change "
- + change.getId() + " of " + parentState.getProject().getName(), err);
+ for (Term[] template : env.all(
+ "gerrit", "can_submit",
+ submitRule,
+ new VariableTerm())) {
+ results.add(template[1]);
}
+ } catch (PrologException err) {
+ return logRuleError("Exception calling " + submitRule + " on change "
+ + change.getId() + " of " + getProject().getName(), err);
+ } catch (RuntimeException err) {
+ return logRuleError("Exception calling " + submitRule + " on change "
+ + change.getId() + " of " + getProject().getName(), err);
}
- parentState = parentState.getParentState();
- childEnv = parentEnv;
+ ProjectState parentState = projectState.getParentState();
+ PrologEnvironment childEnv = env;
+ Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
+ projectsSeen.add(getProject().getNameKey());
+
+ while (parentState != null) {
+ if (!projectsSeen.add(parentState.getProject().getNameKey())) {
+ //parent has been seen before, stop walk up inheritance tree
+ break;
+ }
+ PrologEnvironment parentEnv;
+ try {
+ parentEnv = parentState.newPrologEnvironment();
+ } catch (CompileException err) {
+ return logRuleError("Cannot consult rules.pl for "
+ + parentState.getProject().getName(), err);
+ }
+
+ parentEnv.copyStoredValues(childEnv);
+ Term filterRule =
+ parentEnv.once("gerrit", "locate_submit_filter", new VariableTerm());
+ if (filterRule != null) {
+ try {
+ Term resultsTerm = toListTerm(results);
+ results.clear();
+ Term[] template = parentEnv.once(
+ "gerrit", "filter_submit_results",
+ filterRule,
+ resultsTerm,
+ new VariableTerm());
+ results.addAll(((ListTerm) template[2]).toJava());
+ } catch (PrologException err) {
+ return logRuleError("Exception calling " + filterRule + " on change "
+ + change.getId() + " of " + parentState.getProject().getName(), err);
+ } catch (RuntimeException err) {
+ return logRuleError("Exception calling " + filterRule + " on change "
+ + change.getId() + " of " + parentState.getProject().getName(), err);
+ }
+ }
+
+ parentState = parentState.getParentState();
+ childEnv = parentEnv;
+ }
+ } finally {
+ env.close();
}
if (results.isEmpty()) {