Merge branch 'stable-2.8'
* stable-2.8:
Update buck
Remove temporary files created by --list-plugins operation
--list-plugins should not start init step
Handle KeyboardInterrupt in Buck build wrapper scripts
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index 66bd845..f7a784f 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -17,6 +17,7 @@
[--commit-message]
[--dependencies]
[--submit-records]
+ [--all-reviewers]
[--]
<query>
[limit:<n>]
@@ -89,6 +90,10 @@
Show information about patch sets which depend on, or are needed by,
each patch set.
+--all-reviewers::
+ Show the name and email of all reviewers which are added to a change
+ (irrespective of whether they have been voting on that change or not).
+
--submit-records::
Show submit record information about the change, which
includes whether the change meets the criteria for submission
diff --git a/Documentation/config-gitweb.txt b/Documentation/config-gitweb.txt
index 7ba15b8..711174f 100644
--- a/Documentation/config-gitweb.txt
+++ b/Documentation/config-gitweb.txt
@@ -245,6 +245,10 @@
After updating `'$site_path'/etc/gerrit.config`, the Gerrit server must
be restarted and clients must reload the host page to see the change.
+Note that when using a custom gitweb configuration, values must be
+specified for all of the `project`, `revision`, `branch` and `filehistory`
+settings, otherwise the configuration will not be used.
+
Access Control
++++++++++++++
diff --git a/Documentation/dev-inspector.txt b/Documentation/dev-inspector.txt
new file mode 100644
index 0000000..485ad7b
--- /dev/null
+++ b/Documentation/dev-inspector.txt
@@ -0,0 +1,302 @@
+Gerrit Inspector
+================
+
+NAME
+----
+Gerrit Inspector - Interactive Jython environment for Gerrit
+
+SYNOPSIS
+--------
+[verse]
+'java' -jar gerrit.war 'daemon'
+ -d <SITE_PATH>
+ [\--enable-httpd | \--disable-httpd]
+ [\--enable-sshd | \--disable-sshd]
+ [\--console-log]
+ [\--slave]
+ -s
+
+DESCRIPTION
+-----------
+Runs the Gerrit network daemon on the local system as described
+in the link:pgm-daemon.html[Daemon documentation], additionally
+starting an interactive Jython shell for inspection
+and troubleshooting of live data of the Gerrit instance.
+
+CAUTION: Gerrit Inspector works directly on instances of Java Virtual
+Machine objects and it is possible to read and write instance
+members as well as invoke Java functions. Access is granted
+also to 'private' and 'protected' members. Therefore it is possible
+to introduce changes to the internal state of the system in
+an inconsistent way. Care must be taken not to break the running system
+and/or destroy the data.
+
+INSTALLATION
+------------
+
+Gerrit Inspector requires Jython library ('jython.jar') to be installed
+in the '$site_path/lib' directory. Jython, a Python interpreter for
+the Java Virtual Machine, can be obtained from the http://www.jython.org/
+website. Only 'jython.jar' file is needed, installation of Jython libraries
+is optional. Gerrit Inspector has been tested with Jython 2.5.2 but
+might work an earlier version.
+
+STARTUP
+-------
+
+During startup Jython examines Java libraries found on the classpath.
+While libraries are inspected a large amount of messages is displayed on the console:
+
+----
+*sys-package-mgr*: processing new jar, '/home/user/.gerritcodereview/tmp/gerrit_4890671371398741854_app/sshd-core-0.5.1-r1095809.jar'
+----
+
+After this a system-wide embedded initialization script is started. This script
+is contained in the gerrit's WAR archive. This script produces output similar to
+the following on the console:
+
+----
+"Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
+"m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
+"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
+"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
+
+Welcome to the Gerrit Inspector
+Enter help() to see the above again, EOF to quit and stop Gerrit
+----
+
+Then an optional user startup script is processed. It should be
+located in the gerrit user home directory as '.gerritcodereview/Startup.py'.
+
+This script can access all variables defined in the system (such
+as the ones displayed by the initialization script as shown above).
+Variables and functions defined by the startup scripts are available for
+the interactive interpreter.
+
+When interactive interpreter exits (by issuing EOF on the command line),
+a whole Gerrit instance is shut down gracefully.
+
+USING THE INTERPRETER
+---------------------
+
+Gerrit Inspector launches Jython interpreter in the context of the Gerrit
+Java Virtual Machine. All core facilities of the Jython (and Python)
+language are available to the user.
+
+Additional facilities can be provided, for example a 'Lib' directory from the
+Jython distribution can be installed under '$site_path/lib/Lib' to provide
+access to many standard Python modules. Jython can also use additional Java
+classes and libraries and most of the Python modules and scripts.
+
+The Inspector has by default access to classes and object instances available
+in the Java Virtual Machine. Objects are introspected and *private* and *protected*
+members are also available.
+
+For more information on using Jython, especially with regards to its limitations
+in interfacing to the Java Virtual Machine, please refer to the
+http://www.jython.org/[Jython documentation].
+
+After sucessful initialization it is possible to examine components of
+Java packages, classes and live instances.
+
+----
+>>> import com.google.inject
+>>> dir(com.google.inject)
+['AbstractModule', 'Binder', 'Binding', 'BindingAnnotation', 'ConfigurationException', 'CreationException', 'Exposed', 'Guice', 'ImplementedBy', 'Inject', 'Injector', 'Key', 'MembersInjector', 'Module', 'OutOfScopeException', 'PrivateBinder', 'PrivateModule', 'ProvidedBy', 'Provider', 'Provides', 'ProvisionException', 'Scope', 'ScopeAnnotation', 'Scopes', 'Singleton', 'Stage', 'TypeLiteral', '__name__', 'assistedinject', 'binder', 'internal', 'matcher', 'name', 'servlet', 'spi', 'util']
+>>> type(com.google.inject)
+<type 'javapackage'>
+>>> dir(com.google.inject.Guice)
+['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__',
+'__eq__', '__getattribute__', '__hash__', '__init__', '__ne__',
+'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
+'__str__', '__unicode__', 'class', 'clone', 'createInjector',
+'equals', 'finalize', 'getClass', 'hashCode', 'notify', 'notifyAll',
+'registerNatives', 'toString', 'wait']
+----
+
+Startup script provides some convenient variables to access some global Gerrit components,
+for example a connection to the review database is kept open:
+
+----
+>>> ds
+org.apache.commons.dbcp.BasicDataSource@61db2215
+>>> ds.driverClassName
+u'org.postgresql.Driver'
+>>> ds.dataSource
+org.apache.commons.dbcp.PoolingDataSource@23226fe1
+>>> ds.dataSource.connection
+jdbc:postgresql://localhost/reviewdb, UserName=rv, PostgreSQL Native Driver
+----
+
+It is also possible to interact with the ORM layer:
+
+----
+>>> db = schk.schema.open()
+>>> db
+com.google.gerrit.reviewdb.server.ReviewDb_Schema_GwtOrm$$28@24cbbdf3
+>>> db.getDialect()
+com.google.gwtorm.schema.sql.DialectPostgreSQL@4de07d3e
+>>> for x in db.patchSets().iterateAllEntities():
+... print x
+...
+[PatchSet 1,1]
+[PatchSet 2,1]
+[PatchSet 3,1]
+[PatchSet 4,1]
+[PatchSet 5,1]
+[PatchSet 6,1]
+[PatchSet 7,1]
+[PatchSet 8,1]
+[PatchSet 6,2]
+>>> for x in db.patchComments().iterateAllEntities():
+... print x
+com.google.gerrit.reviewdb.client.PatchLineComment@5381298a
+com.google.gerrit.reviewdb.client.PatchLineComment@44ce4dda
+com.google.gerrit.reviewdb.client.PatchLineComment@44594680
+>>> dir(com.google.gerrit.reviewdb.client.PatchLineComment)
+['Key', 'STATUS_DRAFT', 'STATUS_PUBLISHED', 'Status', '__class__',
+'__copy__', '__deepcopy__', '__delattr__', '__doc__', '__eq__',
+'__getattribute__', '__hash__', '__init__', '__ne__', '__new__',
+'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
+'__unicode__', 'author', 'class', 'clone', 'equals', 'finalize',
+'getAuthor', 'getClass', 'getKey', 'getLine', 'getMessage',
+'getParentUuid', 'getSide', 'getStatus', 'getWrittenOn', 'hashCode',
+'key', 'line', 'lineNbr', 'message', 'notify', 'notifyAll',
+'parentUuid', 'registerNatives', 'setMessage', 'setSide', 'setStatus',
+'side', 'status', 'toString', 'updated', 'wait', 'writtenOn']
+>>> for x in db.patchComments().iterateAllEntities():
+... print x.status, x.line, x.message
+...
+P 2 I like it!
+P 2 more
+P 1 better
+----
+
+A built-in *help()* function provides values of global variables
+defined in the interpreter:
+
+----
+>>> help()
+"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
+"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
+"m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
+"q" is "com.google.gerrit.reviewdb.server.ReviewDb_Schema_GwtOrm$$23@56d3384d"
+"Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
+"x" is "com.google.gerrit.reviewdb.client.PatchLineComment@518acfa7"
+"z" is "com.google.gwtorm.jdbc.Database@668dfd9b"
+"db" is "com.google.gerrit.reviewdb.server.ReviewDb_Schema_GwtOrm$$23@6a3ccb09"
+
+Welcome to the Gerrit Inspector
+Enter help() to see the above again, EOF to quit and stop Gerrit
+----
+
+Java and Python exceptions are intercepted by the Inspector:
+----
+>>> import java.lang.RuntimeException
+>>> raise java.lang.RuntimeException("Exiting")
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
+ at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
+ at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
+ at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
+ at org.python.core.PyReflectedConstructor.constructProxy(PyReflectedConstructor.java:210)
+
+java.lang.RuntimeException: java.lang.RuntimeException: Exiting
+>>>
+----
+
+To exit the interpreter, use EOF character (Ctrl-D on Unix systems, Ctrl-Z on Windows).
+
+It is also possible to shut down the JVM by using *System.exit()*
+
+----
+>>> import java.lang.System
+>>> java.lang.System.exit(1)
+----
+
+And Gerrit should shut down all its subsystems and exit:
+
+----
+[2012-04-17 15:31:08,458] INFO com.google.gerrit.pgm.Daemon : caught shutdown, cleaning up
+----
+
+TROUBLESHOOTING
+---------------
+
+Gerrit Inspector is logging to the Gerrit error log.
+
+A successful startup is indicated in the logfile:
+
+----
+ [2012-04-17 13:43:44,888] INFO com.google.gerrit.pgm.shell.JythonShell : Jython shell instance created.
+----
+
+If 'jython.jar' library is not available, Gerrit refuses to start when given *-s* option:
+
+----
+[2012-04-17 13:57:29,611] ERROR com.google.gerrit.pgm.Daemon : Unable to start daemon
+com.google.inject.ProvisionException: Guice provision errors:
+
+1) Error injecting constructor, java.lang.UnsupportedOperationException: Cannot create Jython shell: Class org.python.util.InteractiveConsole not found
+ (You might need to install jython.jar in the lib directory)
+ at com.google.gerrit.pgm.shell.JythonShell.<init>(JythonShell.java:47)
+ while locating com.google.gerrit.pgm.shell.JythonShell
+ while locating com.google.gerrit.pgm.shell.InteractiveShell
+----
+
+Errors during processing of the startup script, 'Startup.py', are logged
+to the error log:
+
+----
+[2012-04-17 14:20:30,558] INFO com.google.gerrit.pgm.shell.JythonShell : Jython shell instance created.
+[2012-04-17 14:20:38,005] ERROR com.google.gerrit.pgm.shell.JythonShell : Exception occured while loading file Startup.py :
+java.lang.reflect.InvocationTargetException
+ at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
+ at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+ at java.lang.reflect.Method.invoke(Method.java:616)
+ at com.google.gerrit.pgm.shell.JythonShell.runMethod0(JythonShell.java:112)
+ at com.google.gerrit.pgm.shell.JythonShell.execFile(JythonShell.java:194)
+ at com.google.gerrit.pgm.shell.JythonShell.reload(JythonShell.java:178)
+ at com.google.gerrit.pgm.shell.JythonShell.run(JythonShell.java:152)
+ at com.google.gerrit.pgm.Daemon.run(Daemon.java:190)
+ at com.google.gerrit.pgm.util.AbstractProgram.main(AbstractProgram.java:67)
+ at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
+ at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+ at java.lang.reflect.Method.invoke(Method.java:616)
+ at com.google.gerrit.launcher.GerritLauncher.invokeProgram(GerritLauncher.java:167)
+ at com.google.gerrit.launcher.GerritLauncher.mainImpl(GerritLauncher.java:91)
+ at com.google.gerrit.launcher.GerritLauncher.main(GerritLauncher.java:49)
+ at Main.main(Main.java:25)
+Caused by: Traceback (most recent call last):
+ File "/home/user/.gerritcodereview/Startup.py", line 1, in <module>
+ Test
+NameError: name 'Test' is not defined
+----
+
+Those errors are non-fatal. System and user scripts can be loaded again
+by issuing the following command in the Gerrit Inspector console:
+
+----
+Shell.reload()
+----
+
+LOGGING
+-------
+Error and warning messages from the server are automatically written
+to the log file under '$site_path/logs/error_log'.
+
+Output and error messages (including Java and Python exceptions)
+resulting from interactive work are logged to the console.
+
+KNOWN ISSUES
+------------
+The Inspector does not yet recognize Google Guice bindings.
+
+IMPORTANT: Using the Inspector may void your warranty.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index ced8648..728c8dc 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -106,6 +106,43 @@
java -jar buck-out/gen/gerrit.war daemon -d ../test_site
----
+Running the Daemon with Gerrit Inspector
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+link:dev-inspector.html[Gerrit Inspector] is an interactive scriptable
+environment to inspect and modify internal state of the system.
+
+This environment is available on the system console after
+the system starts. Leaving the Inspector will shutdown the Gerrit
+instance.
+
+The environment allows interactive work as well as running of
+Python scripts for troubleshooting.
+
+Gerrit Inspect can be started by adding '-s' option to the
+command used to launch the daemon:
+
+----
+ java -jar buck-out/gen/gerrit.war daemon -d ../test_site -s
+----
+
+Gerrit Inspector examines Java libraries first, then loads
+its initialization scripts and then starts a command line
+prompt on the console:
+
+----
+ Welcome to the Gerrit Inspector
+ Enter help() to see the above again, EOF to quit and stop Gerrit
+ Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
+ [OpenJDK 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0 running for Gerrit 2.3-rc0-163-g01967ef
+ >>>
+----
+
+With the Inspector enabled Gerrit can be used normally and all
+interfaces (HTTP, SSH etc.) are available.
+
+Care must be taken not to modify internal state of the system
+when using the Inspector.
Querying the Database
~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/json.txt b/Documentation/json.txt
index 2836e5b..9278cde 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -72,6 +72,9 @@
submitRecords:: The <<submitRecord,submitRecord attribute>> contains
information about whether this change has been or can be submitted.
+allReviewers:: List of all reviewers in <<account,account attribute>>
+which are added to a change.
+
[[trackingid]]
trackingid
----------
diff --git a/Documentation/pgm-daemon.txt b/Documentation/pgm-daemon.txt
index ce88ab7..4b920ea 100644
--- a/Documentation/pgm-daemon.txt
+++ b/Documentation/pgm-daemon.txt
@@ -16,6 +16,7 @@
[\--slave]
[\--headless]
[\--init]
+ [-s]
DESCRIPTION
-----------
@@ -68,6 +69,14 @@
Run init before starting the daemon. This will create a new site or
upgrade an existing site.
+\--s::
+ Start link:dev-inspector.html[Gerrit Inspector] on the console, a
+ built-in interactive inspection environment to assist debugging and
+ troubleshooting of Gerrit code.
++
+This options requires 'jython.jar' from the http://www.jython.org[Jython distribution]
+to be present in '$site_path/lib' directory.
+
CONTEXT
-------
This command can only be run on a server which has direct
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 28e361c..2dde16e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -168,7 +168,7 @@
Element userlistElement = HtmlDomUtil.find(doc, "userlist");
ReviewDb db = schema.open();
try {
- ResultSet<Account> accounts = db.accounts().firstNById(5);
+ ResultSet<Account> accounts = db.accounts().firstNById(100);
for (Account a : accounts) {
String displayName;
if (a.getUserName() != null) {
diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
index 2dd20a2..fb117767 100644
--- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -302,6 +302,7 @@
}
private volatile static File myArchive;
+ private static File myHome;
/**
* Locate the JAR/WAR file we were launched from.
@@ -459,42 +460,26 @@
return tmp;
}
+ /**
+ * Provide path to a working directory
+ *
+ * @return local path of the working directory or null if cannot be determined
+ */
+ public static File getHomeDirectory() {
+ if (myHome == null) {
+ myHome = locateHomeDirectory();
+ }
+ return myHome;
+ }
+
+
private static File tmproot() {
File tmp;
String gerritTemp = System.getenv("GERRIT_TMP");
if (gerritTemp != null && gerritTemp.length() > 0) {
tmp = new File(gerritTemp);
} else {
- // Try to find the user's home directory. If we can't find it
- // return null so the JVM's default temporary directory is used
- // instead. This is probably /tmp or /var/tmp.
- //
- String userHome = System.getProperty("user.home");
- if (userHome == null || "".equals(userHome)) {
- userHome = System.getenv("HOME");
- if (userHome == null || "".equals(userHome)) {
- System.err.println("warning: cannot determine home directory");
- System.err.println("warning: using system temporary directory instead");
- return null;
- }
- }
-
- // Ensure the home directory exists. If it doesn't, try to make it.
- //
- final File home = new File(userHome);
- if (!home.exists()) {
- if (home.mkdirs()) {
- System.err.println("warning: created " + home.getAbsolutePath());
- } else {
- System.err.println("warning: " + home.getAbsolutePath() + " not found");
- System.err.println("warning: using system temporary directory instead");
- return null;
- }
- }
-
- // Use $HOME/.gerritcodereview/tmp for our temporary file area.
- //
- tmp = new File(new File(home, ".gerritcodereview"), "tmp");
+ tmp = new File(getHomeDirectory(), "tmp");
}
if (!tmp.exists() && !tmp.mkdirs()) {
System.err.println("warning: cannot create " + tmp.getAbsolutePath());
@@ -527,6 +512,49 @@
}
}
+ private static File locateHomeDirectory() {
+ // Try to find the user's home directory. If we can't find it
+ // return null so the JVM's default temporary directory is used
+ // instead. This is probably /tmp or /var/tmp.
+ //
+ String userHome = System.getProperty("user.home");
+ if (userHome == null || "".equals(userHome)) {
+ userHome = System.getenv("HOME");
+ if (userHome == null || "".equals(userHome)) {
+ System.err.println("warning: cannot determine home directory");
+ System.err.println("warning: using system temporary directory instead");
+ return null;
+ }
+ }
+
+ // Ensure the home directory exists. If it doesn't, try to make it.
+ //
+ final File home = new File(userHome);
+ if (!home.exists()) {
+ if (home.mkdirs()) {
+ System.err.println("warning: created " + home.getAbsolutePath());
+ } else {
+ System.err.println("warning: " + home.getAbsolutePath() + " not found");
+ System.err.println("warning: using system temporary directory instead");
+ return null;
+ }
+ }
+
+ // Use $HOME/.gerritcodereview/tmp for our temporary file area.
+ //
+ final File gerrithome = new File(home, ".gerritcodereview");
+ if (!gerrithome.exists() && !gerrithome.mkdirs()) {
+ System.err.println("warning: cannot create " + gerrithome.getAbsolutePath());
+ System.err.println("warning: using system temporary directory instead");
+ return null;
+ }
+ try {
+ return gerrithome.getCanonicalFile();
+ } catch (IOException e) {
+ return gerrithome;
+ }
+ }
+
private GerritLauncher() {
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 891dadc..bb726e3 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -33,6 +33,7 @@
import com.google.gerrit.pgm.http.jetty.JettyEnv;
import com.google.gerrit.pgm.http.jetty.JettyModule;
import com.google.gerrit.pgm.http.jetty.ProjectQoSFilter;
+import com.google.gerrit.pgm.shell.JythonShell;
import com.google.gerrit.pgm.util.ErrorLogFile;
import com.google.gerrit.pgm.util.GarbageCollectionLogFile;
import com.google.gerrit.pgm.util.LogFileCompressor;
@@ -57,6 +58,7 @@
import com.google.gerrit.server.patch.IntraLineWorkerPool;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
import com.google.gerrit.server.plugins.PluginRestApiModule;
+import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.SchemaVersionCheck;
import com.google.gerrit.server.ssh.NoSshKeyCache;
import com.google.gerrit.server.ssh.NoSshModule;
@@ -109,6 +111,9 @@
@Option(name = "--console-log", usage = "Log to console (not $site_path/logs)")
private boolean consoleLog;
+ @Option(name = "-s", usage = "Start interactive shell")
+ private boolean inspector;
+
@Option(name = "--run-id", usage = "Cookie to store in $site_path/logs/gerrit.run")
private String runId;
@@ -224,7 +229,15 @@
serverStarted.run();
}
- RuntimeShutdown.waitFor();
+ if (inspector) {
+ JythonShell shell = new JythonShell();
+ shell.set("m", manager);
+ shell.set("ds", dbInjector.getInstance(DataSourceProvider.class));
+ shell.set("schk", dbInjector.getInstance(SchemaVersionCheck.class));
+ shell.run();
+ } else {
+ RuntimeShutdown.waitFor();
+ }
return 0;
} catch (Throwable err) {
log.error("Unable to start daemon", err);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
new file mode 100644
index 0000000..4ad91d9
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/shell/JythonShell.java
@@ -0,0 +1,221 @@
+// Copyright (C) 2013 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.pgm.shell;
+
+import com.google.gerrit.launcher.GerritLauncher;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Properties;
+
+public class JythonShell {
+ private static final Logger log = LoggerFactory.getLogger(JythonShell.class);
+ private static final String STARTUP_RESOURCE = "com/google/gerrit/pgm/Startup.py";
+ private static final String STARTUP_FILE = "Startup.py";
+
+ private Class<?> console;
+ private Class<?> pyObject;
+ private Class<?> pySystemState;
+ private Object shell;
+ private ArrayList <String> injectedVariables;
+
+ public JythonShell() {
+ Properties env = new Properties();
+ // Let us inspect private class members
+ env.setProperty("python.security.respectJavaAccessibility", "false");
+
+ File home = GerritLauncher.getHomeDirectory();
+ if (home != null) {
+ env.setProperty("python.cachedir", new File(home, "jythoncache").getPath());
+ }
+
+ // For package introspection and "import com.google" to work,
+ // Jython needs to inspect actual .jar files (not just classloader)
+ StringBuilder classPath = new StringBuilder();
+ final ClassLoader cl = getClass().getClassLoader();
+ if (cl instanceof java.net.URLClassLoader) {
+ URLClassLoader ucl = (URLClassLoader) cl;
+ for (URL u : ucl.getURLs()) {
+ if ("file".equals(u.getProtocol())) {
+ if (classPath.length() > 0) {
+ classPath.append(java.io.File.pathSeparatorChar);
+ }
+ classPath.append(u.getFile());
+ }
+ }
+ }
+ env.setProperty("java.class.path", classPath.toString());
+
+ console = findClass("org.python.util.InteractiveConsole");
+ pyObject = findClass("org.python.core.PyObject");
+ pySystemState = findClass("org.python.core.PySystemState");
+
+ runMethod(pySystemState, pySystemState, "initialize",
+ new Class[] { Properties.class, Properties.class },
+ new Object[] { null, env }
+ );
+
+ try {
+ shell = console.newInstance();
+ log.info("Jython shell instance created.");
+ } catch (InstantiationException e) {
+ throw noInterpreter(e);
+ } catch (IllegalAccessException e) {
+ throw noInterpreter(e);
+ }
+ injectedVariables = new ArrayList<String>();
+ set("Shell", this);
+ }
+
+ protected Object runMethod0(Class<?> klazz, Object instance,
+ String name, Class<?>[] sig, Object[] args)
+ throws InvocationTargetException {
+ try {
+ Method m;
+ m = klazz.getMethod(name, sig);
+ return m.invoke(instance, args);
+ } catch (NoSuchMethodException e) {
+ throw cannotStart(e);
+ } catch (SecurityException e) {
+ throw cannotStart(e);
+ } catch (IllegalArgumentException e) {
+ throw cannotStart(e);
+ } catch (IllegalAccessException e) {
+ throw cannotStart(e);
+ }
+ }
+
+ protected Object runMethod(Class<?> klazz, Object instance,
+ String name, Class<?>[] sig, Object[] args) {
+ try {
+ return runMethod0(klazz, instance, name, sig, args);
+ } catch (InvocationTargetException e) {
+ throw cannotStart(e);
+ }
+ }
+
+ protected Object runInterpreter(String name, Class<?>[] sig, Object[] args) {
+ return runMethod(console, shell, name, sig, args);
+ }
+
+ protected String getDefaultBanner() {
+ return (String)runInterpreter("getDefaultBanner",
+ new Class[] { }, new Object[] { });
+ }
+
+ protected void printInjectedVariable(String id) {
+ runInterpreter("exec",
+ new Class[] { String.class },
+ new Object[] { "print '\"%s\" is \"%s\"' % (\"" + id + "\", " + id + ")" }
+ );
+ }
+
+ public void run() {
+ for (String key : injectedVariables) {
+ printInjectedVariable(key);
+ }
+ reload();
+ runInterpreter("interact",
+ new Class[] { String.class, pyObject },
+ new Object[] { getDefaultBanner() +
+ " running for Gerrit " + com.google.gerrit.common.Version.getVersion(),
+ null });
+ }
+
+ public void set(String key, Object content) {
+ runInterpreter("set",
+ new Class[] { String.class, Object.class },
+ new Object[] { key, content }
+ );
+ injectedVariables.add(key);
+ }
+
+ private static Class<?> findClass(String klazzname) {
+ try {
+ return Class.forName(klazzname);
+ } catch (ClassNotFoundException e) {
+ throw noShell("Class " + klazzname + " not found", e);
+ }
+ }
+
+ public void reload() {
+ execResource(STARTUP_RESOURCE);
+ execFile(GerritLauncher.getHomeDirectory(), STARTUP_FILE);
+ }
+
+ protected void execResource(final String p) {
+ InputStream in = JythonShell.class.getClassLoader().getResourceAsStream(p);
+ if (in != null) {
+ execStream(in, "resource " + p);
+ } else {
+ log.error("Cannot load resource " + p);
+ }
+ }
+
+ protected void execFile(final File parent, final String p) {
+ try {
+ File script = new File(parent, p);
+ if (script.canExecute()) {
+ runMethod0(console, shell, "execfile",
+ new Class[] { String.class },
+ new Object[] { script.getAbsolutePath() }
+ );
+ } else {
+ log.info("User initialization file "
+ + script.getAbsolutePath()
+ + " is not found or not executable");
+ }
+ } catch (InvocationTargetException e) {
+ log.error("Exception occured while loading file " + p + " : ", e);
+ } catch (SecurityException e) {
+ log.error("SecurityException occured while loading file " + p + " : ", e);
+ }
+ }
+
+ protected void execStream(final InputStream in, final String p) {
+ try {
+ runMethod0(console, shell, "execfile",
+ new Class[] { InputStream.class, String.class },
+ new Object[] { in, p }
+ );
+ } catch (InvocationTargetException e) {
+ log.error("Exception occured while loading " + p + " : ", e);
+ }
+ }
+
+ private static UnsupportedOperationException noShell(final String m, Throwable why) {
+ final String prefix = "Cannot create Jython shell: ";
+ final String postfix = "\n (You might need to install jython.jar in the lib directory)";
+ return new UnsupportedOperationException(prefix + m + postfix, why);
+ }
+
+ private static UnsupportedOperationException noInterpreter(Throwable why) {
+ final String msg = "Cannot create Python interpreter";
+ return noShell(msg, why);
+ }
+
+ private static UnsupportedOperationException cannotStart(Throwable why) {
+ final String msg = "Cannot start Jython shell";
+ return new UnsupportedOperationException(msg, why);
+ }
+}
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/Startup.py b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/Startup.py
new file mode 100644
index 0000000..92d6e56
--- /dev/null
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/Startup.py
@@ -0,0 +1,31 @@
+# Copyright (C) 2013 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.
+
+# -----------------------------------------------------------------------
+# Startup script for Gerrit Inspector - a Jython introspector
+# -----------------------------------------------------------------------
+
+import sys
+
+def help():
+ for (n, v) in vars(sys.modules['__main__']).items():
+ if not n.startswith("__") and not n in ['help', 'reload'] \
+ and str(type(v)) != "<type 'javapackage'>" \
+ and not str(v).startswith("<module"):
+ print "\"%s\" is \"%s\"" % (n, v)
+ print
+ print "Welcome to the Gerrit Inspector"
+ print "Enter help() to see the above again, EOF to quit and stop Gerrit"
+
+help()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
index 7339829..5f5fd33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
@@ -43,4 +43,5 @@
public List<DependencyAttribute> dependsOn;
public List<DependencyAttribute> neededBy;
public List<SubmitRecordAttribute> submitRecords;
+ public List<AccountAttribute> allReviewers;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index 98e803f..58d16dc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.events;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
@@ -69,6 +71,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
@Singleton
public class EventFactory {
@@ -158,6 +161,29 @@
}
/**
+ * Add allReviewers to an existing ChangeAttribute.
+ *
+ * @param a
+ * @param change
+ */
+ public void addAllReviewers(ChangeAttribute a, Change change)
+ throws OrmException {
+ List<PatchSetApproval> approvals =
+ db.get().patchSetApprovals().byChange(change.getId()).toList();
+ if (!approvals.isEmpty()) {
+ a.allReviewers = Lists.newArrayList();
+ Set<Account.Id> seen = Sets.newHashSet();
+ for (PatchSetApproval psa : approvals) {
+ Account.Id id = psa.getAccountId();
+ if (!seen.contains(id)) {
+ seen.add(id);
+ a.allReviewers.add(asAccountAttribute(id));
+ }
+ }
+ }
+ }
+
+ /**
* Add submitRecords to an existing ChangeAttribute.
*
* @param ca
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index 5624f45..e569ff5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -113,6 +113,7 @@
private boolean includeCommitMessage;
private boolean includeDependencies;
private boolean includeSubmitRecords;
+ private boolean includeAllReviewers;
private OutputStream outputStream = DisabledOutputStream.INSTANCE;
private PrintWriter out;
@@ -200,6 +201,10 @@
includeSubmitRecords = on;
}
+ public void setIncludeAllReviewers(boolean on) {
+ includeAllReviewers = on;
+ }
+
public void setOutput(OutputStream out, OutputFormat fmt) {
this.outputStream = out;
this.outputFormat = fmt;
@@ -304,6 +309,10 @@
eventFactory.extend(c, d.getChange());
eventFactory.addTrackingIds(c, d.trackingIds(db));
+ if (includeAllReviewers) {
+ eventFactory.addAllReviewers(c, d.getChange());
+ }
+
if (includeSubmitRecords) {
PatchSet.Id psId = d.getChange().currentPatchSetId();
PatchSet patchSet = db.get().patchSets().get(psId);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
index 185bb67..af42e1b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
@@ -72,6 +72,11 @@
processor.setIncludeDependencies(on);
}
+ @Option(name = "--all-reviewers", usage = "Include all reviewers")
+ void setAllReviewers(boolean on) {
+ processor.setIncludeAllReviewers(on);
+ }
+
@Option(name = "--submit-records", usage = "Include submit and label status")
void setSubmitRecords(boolean on) {
processor.setIncludeSubmitRecords(on);
diff --git a/plugins/download-commands b/plugins/download-commands
index eaeb186..32254d3 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit eaeb1862180782bd5f696c79379a909dce7320b7
+Subproject commit 32254d3b75d65aceb485950fbf30d464574e02fc