Merge branch 'stable-3.1' * stable-3.1: Adapt to the latest Gerrit on stable-3.0 Do not block authentication operation during websession event send Change-Id: I6e0f944afe57e53678493c3f37ed026b867d5f41
diff --git a/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSession.java b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSession.java index 733581c..00a90ee 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSession.java +++ b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSession.java
@@ -22,9 +22,11 @@ import com.google.gerrit.httpd.WebSessionManagerFactory; import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.IdentifiedUser.RequestFactory; +import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.events.EventTypes; +import com.google.gerrit.server.git.WorkQueue; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Scopes; @@ -34,11 +36,20 @@ import com.googlesource.gerrit.plugins.websession.broker.log.Log4jWebSessionLogger; import com.googlesource.gerrit.plugins.websession.broker.log.WebSessionLogger; import java.lang.annotation.Annotation; +import java.util.concurrent.ExecutorService; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class BrokerBasedWebSession extends CacheBasedWebSession { public static class Module extends CacheModule { + private WorkQueue workQueue; + private BrokerBasedWebSessionConfiguration configuration; + + @Inject + public Module(WorkQueue workQueue, BrokerBasedWebSessionConfiguration configuration) { + this.workQueue = workQueue; + this.configuration = configuration; + } @Override protected void configure() { @@ -50,6 +61,12 @@ .to(BrokerBasedWebSession.class) .in(RequestScoped.class); + bind(ExecutorService.class) + .annotatedWith(WebSessionProducerExecutor.class) + .toInstance( + workQueue.createQueue( + configuration.getNumberOfThreads(), "websession-events-producer")); + listener(BrokerBasedWebSessionCache.class); listener(BrokerBasedWebSessionCacheCleaner.class); listener(Log4jWebSessionLogger.class); @@ -71,13 +88,15 @@ BrokerBasedWebSessionCache cache, AuthConfig authConfig, Provider<AnonymousUser> anonymousProvider, - RequestFactory identified) { + RequestFactory identified, + AccountCache byIdCache) { super( request.get(), response.get(), managerFactory.create(cache), authConfig, anonymousProvider, - identified); + identified, + byIdCache); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCache.java b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCache.java index 147fa93..678d246 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCache.java +++ b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCache.java
@@ -47,6 +47,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; @Singleton public class BrokerBasedWebSessionCache @@ -59,6 +60,7 @@ String webSessionTopicName; DynamicItem<BrokerApi> brokerApi; TimeMachine timeMachine; + ExecutorService executor; private final WebSessionLogger webSessionLogger; @Inject @@ -68,12 +70,14 @@ TimeMachine timeMachine, PluginConfigFactory cfg, @PluginName String pluginName, - WebSessionLogger webSessionLogger) { + WebSessionLogger webSessionLogger, + @WebSessionProducerExecutor ExecutorService executor) { this.cache = cache; this.brokerApi = brokerApi; this.timeMachine = timeMachine; this.webSessionTopicName = getWebSessionTopicName(cfg, pluginName); this.webSessionLogger = webSessionLogger; + this.executor = executor; } protected void processMessage(EventMessage message) { @@ -184,26 +188,11 @@ } private void sendEvent(String key, Val value, WebSessionEvent.Operation operation) { - boolean succeeded = false; - try (ByteArrayOutputStream out = new ByteArrayOutputStream(); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(out)) { - - objectOutputStream.writeObject(value); - out.flush(); - byte[] serializedObject = out.toByteArray(); - WebSessionEvent webSessionEvent = new WebSessionEvent(key, serializedObject, operation); - EventMessage message = brokerApi.get().newMessage(UUID.randomUUID(), webSessionEvent); - succeeded = brokerApi.get().send(webSessionTopicName, message); - if (succeeded) { - webSessionLogger.log( - Direction.PUBLISH, webSessionTopicName, webSessionEvent, Optional.ofNullable(value)); - } else { - logger.atSevere().log( - "Cannot send web-session message for '%s Topic: '%s'", key, webSessionTopicName); - } - } catch (IOException e) { + try { + executor.execute(new WebSessionEventTask(key, value, operation)); + } catch (RuntimeException e) { logger.atSevere().withCause(e).log( - "Cannot serialize event for account id '%s': [Exception: %s]", value.getAccountId()); + "Cannot send web-session message for '%s Topic: '%s'", key, webSessionTopicName); } } @@ -243,4 +232,41 @@ @Override public void stop() {} + + private class WebSessionEventTask implements Runnable { + private String key; + private Val value; + private WebSessionEvent.Operation operation; + + public WebSessionEventTask(String key, Val value, WebSessionEvent.Operation operation) { + this.key = key; + this.value = value; + this.operation = operation; + } + + @Override + public void run() { + boolean succeeded = false; + try (ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(out)) { + + objectOutputStream.writeObject(value); + out.flush(); + byte[] serializedObject = out.toByteArray(); + WebSessionEvent webSessionEvent = new WebSessionEvent(key, serializedObject, operation); + EventMessage message = brokerApi.get().newMessage(UUID.randomUUID(), webSessionEvent); + succeeded = brokerApi.get().send(webSessionTopicName, message); + if (succeeded) { + webSessionLogger.log( + Direction.PUBLISH, webSessionTopicName, webSessionEvent, Optional.ofNullable(value)); + } else { + logger.atSevere().log( + "Cannot send web-session message for '%s Topic: '%s'", key, webSessionTopicName); + } + } catch (IOException e) { + logger.atSevere().withCause(e).log( + "Cannot serialize event for account id '%s': [Exception: %s]", value.getAccountId()); + } + } + } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionConfiguration.java new file mode 100644 index 0000000..8f103a7 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionConfiguration.java
@@ -0,0 +1,40 @@ +// Copyright (C) 2019 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.googlesource.gerrit.plugins.websession.broker; + +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.server.config.PluginConfig; +import com.google.gerrit.server.config.PluginConfigFactory; +import com.google.inject.Inject; + +public class BrokerBasedWebSessionConfiguration { + + private static final int DEFAULT_NUMBER_OF_THREADS = 1; + + private final Integer numberOfThreads; + + @Inject + public BrokerBasedWebSessionConfiguration( + PluginConfigFactory configFactory, @PluginName String pluginName) { + + PluginConfig fromGerritConfig = configFactory.getFromGerritConfig(pluginName); + + this.numberOfThreads = fromGerritConfig.getInt("numberOfThreads", DEFAULT_NUMBER_OF_THREADS); + } + + public Integer getNumberOfThreads() { + return numberOfThreads; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/websession/broker/WebSessionProducerExecutor.java b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/WebSessionProducerExecutor.java new file mode 100644 index 0000000..5d59852 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/websession/broker/WebSessionProducerExecutor.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2019 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.googlesource.gerrit.plugins.websession.broker; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; +import java.lang.annotation.Retention; + +@Retention(RUNTIME) +@BindingAnnotation +public @interface WebSessionProducerExecutor {}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index 7ac2e37..a834904 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -18,6 +18,10 @@ : Name of the topic to use for publishing web session events. Default: gerrit\_web\_session +`plugin.websession-broker.numberOfThreads` +: Number of threads used to send web session events via events-broker. + Default: 1 + `plugin.websession-broker.cleanupInterval` : Frequency of the expired web session cleanup operation. Value should use common time unit suffixes to express their setting:
diff --git a/src/test/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCacheTest.java b/src/test/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCacheTest.java index 60e995e..e6cb20e 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCacheTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/websession/broker/BrokerBasedWebSessionCacheTest.java
@@ -28,6 +28,7 @@ import com.gerritforge.gerrit.eventbroker.EventMessage.Header; import com.google.common.cache.Cache; import com.google.common.collect.Maps; +import com.google.common.util.concurrent.MoreExecutors; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.httpd.WebSessionManager.Val; import com.google.gerrit.server.config.PluginConfig; @@ -40,6 +41,7 @@ import java.time.Instant; import java.util.UUID; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,6 +70,8 @@ 52, 121, 110, 65, 100, 110, 113, 99, 68, 45, 105, 99, 75, 97, 0, 120 }; + ExecutorService executorServce = MoreExecutors.newDirectExecutorService(); + @Mock BrokerApi brokerApi; @Mock Cache<String, Val> cache; @Mock TimeMachine timeMachine; @@ -88,7 +92,7 @@ DynamicItem<BrokerApi> item = DynamicItem.itemOf(BrokerApi.class, brokerApi); objectUnderTest = new BrokerBasedWebSessionCache( - cache, item, timeMachine, cfg, PLUGIN_NAME, webSessionLogger); + cache, item, timeMachine, cfg, PLUGIN_NAME, webSessionLogger, executorServce); } @Test @@ -98,7 +102,6 @@ when(brokerApi.newMessage(any(UUID.class), any(Event.class))).thenReturn(eventMessage); objectUnderTest.put(KEY, value); - verify(brokerApi, times(1)).send(anyString(), eventCaptor.capture()); assertThat(eventCaptor.getValue().getEvent()).isNotNull();