Merge "Introduce Gerrit Inspector: interactive Jython shell"
This commit is contained in:
302
Documentation/dev-inspector.txt
Normal file
302
Documentation/dev-inspector.txt
Normal file
@@ -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]
|
@@ -106,6 +106,43 @@ copying to the test site:
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@@ -16,6 +16,7 @@ SYNOPSIS
|
||||
[\--slave]
|
||||
[\--headless]
|
||||
[\--init]
|
||||
[-s]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -68,6 +69,14 @@ This option automatically implies '\--disable-httpd \--enable-sshd'.
|
||||
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
|
||||
|
@@ -302,6 +302,7 @@ public final class GerritLauncher {
|
||||
}
|
||||
|
||||
private volatile static File myArchive;
|
||||
private static File myHome;
|
||||
|
||||
/**
|
||||
* Locate the JAR/WAR file we were launched from.
|
||||
@@ -459,42 +460,26 @@ public final class GerritLauncher {
|
||||
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 @@ public final class GerritLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import com.google.gerrit.pgm.http.jetty.GetUserFilter;
|
||||
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.mail.SmtpEmailSender;
|
||||
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 @@ public class Daemon extends SiteProgram {
|
||||
@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 @@ public class Daemon extends SiteProgram {
|
||||
serverStarted.run();
|
||||
}
|
||||
|
||||
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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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()
|
Reference in New Issue
Block a user