summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrudhvi Akhil Alahari <prudhvi@codeaurora.org>2021-08-13 21:35:38 +0530
committerPrudhvi Akhil Alahari <prudhvi@codeaurora.org>2021-09-14 10:28:52 +0530
commitac4c305046ca3dc7095f021c6307b4eefae85eff (patch)
tree6df4a4bb9b08add0f4d569c91536a32745d34e4f
parent6cb377f998cf6c3543d25b3250dfca8d0ee18532 (diff)
Fix DynamicOptions to invoke listeners registered to BeanParseListener
The change which added support for plugin fields in GetChange(I9254d46b) was unintentionally assigning the DynamicOptions class object as beanClass. Due to this, if there were any bean parse listeners exported with bean's canonical class name as export name, they were not invoked. Fix this by assigning bean's canonical class name as beanClass in DynamicOptions. Also add documentation and integration tests for this scenario. Change-Id: Ibaafb98be0f940d835f042a74f914741efa259fb
-rw-r--r--Documentation/dev-plugins.txt57
-rw-r--r--java/com/google/gerrit/server/DynamicOptions.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java67
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java69
4 files changed, 194 insertions, 1 deletions
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 8ad55358c7..21903261dc 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -796,6 +796,63 @@ public class SshModule extends AbstractModule {
}
----
+Plugins can receive a bean object for each of the gerrit ssh and the REST API
+commands by implementing BeanParseListener interface and registering it to a
+command class name in the plugin module's `configure()` method. The below
+example shows a plugin that always limits the number of projects returned
+by the ls-projects SSH command.
+
+[source, java]
+----
+protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjectsCommand.class))
+ .to(ListProjectsCommandBeanListener.class);
+ }
+
+ protected static class ListProjectsCommandBeanListener
+ implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjectsCommand command = (ListProjectsCommand) bean;
+ command.impl.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+}
+----
+
+The below example shows a plugin that always limits the number of projects
+returned by the /projects/ REST API.
+
+[source, java]
+----
+protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjects.class))
+ .to(ListProjectsBeanListener.class);
+ }
+
+ protected static class ListProjectsBeanListener
+ implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjects listProjects = (ListProjects) bean;
+ listProjects.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+}
+----
+
=== Calling Command Options ===
Within an OptionHandler, during the processing of an option, plugins can
diff --git a/java/com/google/gerrit/server/DynamicOptions.java b/java/com/google/gerrit/server/DynamicOptions.java
index 41dc0828b9..ebf4ec64ab 100644
--- a/java/com/google/gerrit/server/DynamicOptions.java
+++ b/java/com/google/gerrit/server/DynamicOptions.java
@@ -213,7 +213,7 @@ public class DynamicOptions {
Class<?> beanClass =
(bean instanceof BeanReceiver)
? ((BeanReceiver) bean).getExportedBeanReceiver()
- : getClass();
+ : bean.getClass();
for (String plugin : dynamicBeans.plugins()) {
Provider<DynamicBean> provider =
dynamicBeans.byPlugin(plugin).get(beanClass.getCanonicalName());
diff --git a/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java b/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java
new file mode 100644
index 0000000000..39f1e8d75e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2021 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.acceptance.rest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.json.OutputFormat;
+import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.server.restapi.project.ListProjects;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.AbstractModule;
+import java.util.Map;
+import org.junit.Test;
+
+public class DynamicOptionsBeanParseListenerIT extends AbstractDaemonTest {
+ private static final Gson GSON = OutputFormat.JSON.newGson();
+
+ @Test
+ public void testBeanParseListener() throws Exception {
+ createProjectOverAPI("project1", project, true, null);
+ createProjectOverAPI("project2", project, true, null);
+ try (AutoCloseable ignored = installPlugin("my-plugin", PluginModule.class)) {
+ assertThat(getProjects(adminRestSession.get("/projects/"))).hasSize(1);
+ }
+ }
+
+ protected Map<String, Object> getProjects(RestResponse res) throws Exception {
+ res.assertOK();
+ return GSON.fromJson(res.getReader(), new TypeToken<Map<String, Object>>() {}.getType());
+ }
+
+ protected static class ListProjectsBeanListener implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjects listProjects = (ListProjects) bean;
+ listProjects.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+
+ protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjects.class))
+ .to(ListProjectsBeanListener.class);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java b/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java
new file mode 100644
index 0000000000..0afdbc6b9e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java
@@ -0,0 +1,69 @@
+// Copyright (C) 2021 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.acceptance.ssh;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.sshd.commands.ListProjectsCommand;
+import com.google.inject.AbstractModule;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+@NoHttpd
+@UseSsh
+public class DynamicOptionsBeanParseListenerIT extends AbstractDaemonTest {
+
+ @Test
+ public void testBeanParseListener() throws Exception {
+ createProjectOverAPI("project1", project, true, null);
+ createProjectOverAPI("project2", project, true, null);
+ try (AutoCloseable ignored = installPlugin("my-plugin", PluginModule.class)) {
+ String output = adminSshSession.exec("gerrit ls-projects");
+ adminSshSession.assertSuccess();
+ assertThat(getProjects(output)).hasSize(1);
+ }
+ }
+
+ protected List<String> getProjects(String sshOutput) {
+ return Arrays.asList(sshOutput.split("\n"));
+ }
+
+ protected static class ListProjectsCommandBeanListener
+ implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjectsCommand command = (ListProjectsCommand) bean;
+ command.impl.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+
+ protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjectsCommand.class))
+ .to(ListProjectsCommandBeanListener.class);
+ }
+ }
+}