From 8c84bd2d14fc168fd4c9feb4b0c84a829a626f7d Mon Sep 17 00:00:00 2001 From: Paladox none Date: Fri, 24 Jan 2020 01:17:49 +0000 Subject: [PATCH 1/4] Fix support for python3 in download_bower.py Was getting "TypeError: write() argument must be str, not bytes" Change-Id: I68ba54f7df1ba4c9ae160442f93a74c837ddc9d3 --- tools/js/download_bower.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/js/download_bower.py b/tools/js/download_bower.py index c9a5df69c7..c1d7d00ce2 100755 --- a/tools/js/download_bower.py +++ b/tools/js/download_bower.py @@ -46,7 +46,8 @@ def bower_info(bower, name, package, version): raise out, err = p.communicate() if p.returncode: - sys.stderr.write(err) + # For python3 support we wrap str around err. + sys.stderr.write(str(err)) raise OSError('Command failed: %s' % ' '.join(cmd)) try: From 4f9264e5826d9067dafe59d6a4c2f3a45b740317 Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Tue, 28 Jan 2020 13:52:52 +0100 Subject: [PATCH 2/4] Fix accountBelongsToRealm implementation It used to search the whole external id's string, instead of looking at the scheme only. In particular, when account deactivation is enabled, this causes deactivation of system accounts with username 'gerrit.xxxxx' (typically used for replication between gerrit instances). Bug: Issue 12243 Change-Id: I6462d87cb55040f5d1bdc949a763b19a9e010861 --- .../gerrit/server/auth/ldap/LdapRealm.java | 2 +- .../gerrit/server/auth/oauth/OAuthRealm.java | 2 +- .../server/auth/ldap/LdapRealmTest.java | 94 +++++++++++++++++++ .../server/auth/oauth/OAuthRealmTest.java | 69 ++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java create mode 100644 javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java diff --git a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java index ed446f218a..b047488097 100644 --- a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java +++ b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java @@ -336,7 +336,7 @@ class LdapRealm extends AbstractRealm { @Override public boolean accountBelongsToRealm(Collection externalIds) { for (ExternalId id : externalIds) { - if (id.toString().contains(SCHEME_GERRIT)) { + if (id.isScheme(SCHEME_GERRIT)) { return true; } } diff --git a/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java b/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java index 54d50f08f4..2cef62d723 100644 --- a/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java +++ b/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java @@ -122,7 +122,7 @@ public class OAuthRealm extends AbstractRealm { @Override public boolean accountBelongsToRealm(Collection externalIds) { for (ExternalId id : externalIds) { - if (id.toString().contains(SCHEME_EXTERNAL)) { + if (id.isScheme(SCHEME_EXTERNAL)) { return true; } } diff --git a/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java new file mode 100644 index 0000000000..13de3e7ad0 --- /dev/null +++ b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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.server.auth.ldap; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT; +import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO; +import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME; +import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_CACHE; +import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_EXIST_CACHE; +import static com.google.gerrit.server.auth.ldap.LdapModule.PARENT_GROUPS_CACHE; +import static com.google.gerrit.server.auth.ldap.LdapModule.USERNAME_CACHE; + +import com.google.common.collect.ImmutableSet; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.server.account.externalids.ExternalId; +import com.google.gerrit.server.cache.CacheModule; +import com.google.gerrit.testing.InMemoryModule; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.TypeLiteral; +import java.util.Arrays; +import java.util.Optional; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; + +public final class LdapRealmTest { + @Inject private LdapRealm ldapRealm = null; + + @Before + public void setUpInjector() throws Exception { + Injector injector = + Guice.createInjector( + new InMemoryModule(), + new CacheModule() { + @Override + protected void configure() { + cache(GROUP_CACHE, String.class, new TypeLiteral>() {}) + .loader(LdapRealm.MemberLoader.class); + cache(USERNAME_CACHE, String.class, new TypeLiteral>() {}) + .loader(LdapRealm.UserLoader.class); + cache(GROUP_EXIST_CACHE, String.class, new TypeLiteral() {}) + .loader(LdapRealm.ExistenceLoader.class); + cache( + PARENT_GROUPS_CACHE, String.class, new TypeLiteral>() {}); + } + }); + injector.injectMembers(this); + } + + private ExternalId id(String scheme, String id) { + return ExternalId.create(scheme, id, new Account.Id(1000)); + } + + private boolean accountBelongsToRealm(ExternalId... ids) { + return ldapRealm.accountBelongsToRealm(Arrays.asList(ids)); + } + + private boolean accountBelongsToRealm(String scheme, String id) { + return accountBelongsToRealm(id(scheme, id)); + } + + @Test + public void accountBelongsToRealm() throws Exception { + assertThat(accountBelongsToRealm(SCHEME_GERRIT, "test")).isTrue(); + assertThat(accountBelongsToRealm(id(SCHEME_USERNAME, "test"), id(SCHEME_GERRIT, "test"))) + .isTrue(); + assertThat(accountBelongsToRealm(id(SCHEME_GERRIT, "test"), id(SCHEME_USERNAME, "test"))) + .isTrue(); + + assertThat(accountBelongsToRealm(SCHEME_USERNAME, "test")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_MAILTO, "foo@bar.com")).isFalse(); + + assertThat(accountBelongsToRealm(SCHEME_USERNAME, "gerrit")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxgerritxx")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_MAILTO, "gerrit.foo@bar.com")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.gerrit@bar.com")).isFalse(); + } +} diff --git a/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java new file mode 100644 index 0000000000..7a0661a59b --- /dev/null +++ b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java @@ -0,0 +1,69 @@ +// Copyright (C) 2020 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.server.auth.oauth; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL; +import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO; +import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME; + +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.server.account.externalids.ExternalId; +import com.google.gerrit.testing.InMemoryModule; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; + +public final class OAuthRealmTest { + @Inject private OAuthRealm oauthRealm = null; + + @Before + public void setUpInjector() throws Exception { + Injector injector = Guice.createInjector(new InMemoryModule()); + injector.injectMembers(this); + } + + private ExternalId id(String scheme, String id) { + return ExternalId.create(scheme, id, new Account.Id(1000)); + } + + private boolean accountBelongsToRealm(ExternalId... ids) { + return oauthRealm.accountBelongsToRealm(Arrays.asList(ids)); + } + + private boolean accountBelongsToRealm(String scheme, String id) { + return accountBelongsToRealm(id(scheme, id)); + } + + @Test + public void accountBelongsToRealm() throws Exception { + assertThat(accountBelongsToRealm(SCHEME_EXTERNAL, "test")).isTrue(); + assertThat(accountBelongsToRealm(id(SCHEME_USERNAME, "test"), id(SCHEME_EXTERNAL, "test"))) + .isTrue(); + assertThat(accountBelongsToRealm(id(SCHEME_EXTERNAL, "test"), id(SCHEME_USERNAME, "test"))) + .isTrue(); + + assertThat(accountBelongsToRealm(SCHEME_USERNAME, "test")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_MAILTO, "foo@bar.com")).isFalse(); + + assertThat(accountBelongsToRealm(SCHEME_USERNAME, "external")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxexternalxx")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_MAILTO, "external.foo@bar.com")).isFalse(); + assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.external@bar.com")).isFalse(); + } +} From a3db8e117d10a91920511d38779dc2c6e5d10fcf Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Tue, 28 Jan 2020 17:02:26 +0100 Subject: [PATCH 3/4] Fix editing name & email for service user A service-user (either created by SSH command or through ServiceUser plugin) is actually not bound to the realm : so it makes no sense to check if the realm supports editing FULL_USERNAME or EMAIL. To overcome, we now check realm capabilities only for users which belong to that realm. Change-Id: Ic4e04936a5d2444bf6b4644a2f8a3838a2686256 --- .../gerrit/server/restapi/account/DeleteEmail.java | 7 +++++-- .../gerrit/server/restapi/account/PutName.java | 12 ++++++++++-- .../gerrit/server/restapi/account/PutUsername.java | 9 +++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java index 6bacde2220..36788c754b 100644 --- a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java +++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java @@ -24,6 +24,7 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountException; @@ -80,12 +81,14 @@ public class DeleteEmail implements RestModifyView public Response apply(IdentifiedUser user, String email) throws ResourceNotFoundException, ResourceConflictException, MethodNotAllowedException, OrmException, IOException, ConfigInvalidException { - if (!realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) { + Account.Id accountId = user.getAccountId(); + if (realm.accountBelongsToRealm(externalIds.byAccount(accountId)) + && !realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) { throw new MethodNotAllowedException("realm does not allow deleting emails"); } Set extIds = - externalIds.byAccount(user.getAccountId()).stream() + externalIds.byAccount(accountId).stream() .filter(e -> email.equals(e.email())) .collect(toSet()); if (extIds.isEmpty()) { diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java index 1e00aaccaf..4918aa38dc 100644 --- a/java/com/google/gerrit/server/restapi/account/PutName.java +++ b/java/com/google/gerrit/server/restapi/account/PutName.java @@ -22,6 +22,7 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.ServerInitiated; @@ -29,6 +30,7 @@ import com.google.gerrit.server.account.AccountResource; import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountsUpdate; import com.google.gerrit.server.account.Realm; +import com.google.gerrit.server.account.externalids.ExternalIds; import com.google.gerrit.server.permissions.GlobalPermission; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackendException; @@ -44,6 +46,7 @@ public class PutName implements RestModifyView { private final Provider self; private final Realm realm; private final PermissionBackend permissionBackend; + private final ExternalIds externalIds; private final Provider accountsUpdateProvider; @Inject @@ -51,10 +54,12 @@ public class PutName implements RestModifyView { Provider self, Realm realm, PermissionBackend permissionBackend, + ExternalIds externalIds, @ServerInitiated Provider accountsUpdateProvider) { this.self = self; this.realm = realm; this.permissionBackend = permissionBackend; + this.externalIds = externalIds; this.accountsUpdateProvider = accountsUpdateProvider; } @@ -71,11 +76,14 @@ public class PutName implements RestModifyView { public Response apply(IdentifiedUser user, NameInput input) throws MethodNotAllowedException, ResourceNotFoundException, IOException, ConfigInvalidException, OrmException { + if (input == null) { input = new NameInput(); } - if (!realm.allowsEdit(AccountFieldName.FULL_NAME)) { + Account.Id accountId = user.getAccountId(); + if (realm.accountBelongsToRealm(externalIds.byAccount(accountId)) + && !realm.allowsEdit(AccountFieldName.FULL_NAME)) { throw new MethodNotAllowedException("realm does not allow editing name"); } @@ -83,7 +91,7 @@ public class PutName implements RestModifyView { AccountState accountState = accountsUpdateProvider .get() - .update("Set Full Name via API", user.getAccountId(), u -> u.setFullName(newName)) + .update("Set Full Name via API", accountId, u -> u.setFullName(newName)) .orElseThrow(() -> new ResourceNotFoundException("account not found")); return Strings.isNullOrEmpty(accountState.getAccount().getFullName()) ? Response.none() diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java index 7fff626d16..bc95153cbe 100644 --- a/java/com/google/gerrit/server/restapi/account/PutUsername.java +++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java @@ -79,10 +79,6 @@ public class PutUsername implements RestModifyView Date: Thu, 30 Jan 2020 02:07:02 +0100 Subject: [PATCH 4/4] Expose jetty threadpool metrics via dropwizard Change-Id: I91496c38683c4b41f11978d832eaacd1f60adcf0 --- .../google/gerrit/metrics/dropwizard/BUILD | 1 + java/com/google/gerrit/pgm/http/jetty/BUILD | 1 + .../gerrit/pgm/http/jetty/JettyMetrics.java | 91 +++++++++++++++++++ .../gerrit/pgm/http/jetty/JettyModule.java | 1 + .../gerrit/pgm/http/jetty/JettyServer.java | 53 ++++++++++- 5 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java diff --git a/java/com/google/gerrit/metrics/dropwizard/BUILD b/java/com/google/gerrit/metrics/dropwizard/BUILD index 4b3859f7a8..307980939f 100644 --- a/java/com/google/gerrit/metrics/dropwizard/BUILD +++ b/java/com/google/gerrit/metrics/dropwizard/BUILD @@ -8,6 +8,7 @@ java_library( "//java/com/google/gerrit/common:annotations", "//java/com/google/gerrit/extensions:api", "//java/com/google/gerrit/metrics", + "//java/com/google/gerrit/pgm/http/jetty", "//java/com/google/gerrit/server", "//lib:args4j", "//lib:guava", diff --git a/java/com/google/gerrit/pgm/http/jetty/BUILD b/java/com/google/gerrit/pgm/http/jetty/BUILD index ea3afe118c..43a8ed92b8 100644 --- a/java/com/google/gerrit/pgm/http/jetty/BUILD +++ b/java/com/google/gerrit/pgm/http/jetty/BUILD @@ -10,6 +10,7 @@ java_library( "//java/com/google/gerrit/httpd", "//java/com/google/gerrit/launcher", "//java/com/google/gerrit/lifecycle", + "//java/com/google/gerrit/metrics", "//java/com/google/gerrit/server", "//java/com/google/gerrit/server/util/time", "//java/com/google/gerrit/sshd", diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java b/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java new file mode 100644 index 0000000000..92edf403d1 --- /dev/null +++ b/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java @@ -0,0 +1,91 @@ +// Copyright (C) 2020 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.http.jetty; + +import com.google.common.collect.ImmutableSet; +import com.google.gerrit.metrics.CallbackMetric; +import com.google.gerrit.metrics.CallbackMetric0; +import com.google.gerrit.metrics.Description; +import com.google.gerrit.metrics.MetricMaker; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +@Singleton +public class JettyMetrics { + + @Inject + JettyMetrics(JettyServer jetty, MetricMaker metrics) { + CallbackMetric0 minPoolSize = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/min_pool_size", + Integer.class, + new Description("Minimum thread pool size").setGauge()); + CallbackMetric0 maxPoolSize = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/max_pool_size", + Integer.class, + new Description("Maximum thread pool size").setGauge()); + CallbackMetric0 poolSize = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/pool_size", + Integer.class, + new Description("Current thread pool size").setGauge()); + CallbackMetric0 idleThreads = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/idle_threads", + Integer.class, + new Description("Idle httpd threads").setGauge().setUnit("threads")); + CallbackMetric0 busyThreads = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/active_threads", + Integer.class, + new Description("Active httpd threads").setGauge().setUnit("threads")); + CallbackMetric0 reservedThreads = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/reserved_threads", + Integer.class, + new Description("Reserved httpd threads").setGauge().setUnit("threads")); + CallbackMetric0 queueSize = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/queue_size", + Integer.class, + new Description("Thread pool queue size").setGauge().setUnit("requests")); + CallbackMetric0 lowOnThreads = + metrics.newCallbackMetric( + "httpd/jetty/threadpool/is_low_on_threads", + Boolean.class, + new Description("Whether thread pool is low on threads").setGauge()); + JettyServer.Metrics jettyMetrics = jetty.getMetrics(); + metrics.newTrigger( + ImmutableSet.>of( + idleThreads, + busyThreads, + reservedThreads, + minPoolSize, + maxPoolSize, + poolSize, + queueSize, + lowOnThreads), + () -> { + minPoolSize.set(jettyMetrics.getMinThreads()); + maxPoolSize.set(jettyMetrics.getMaxThreads()); + poolSize.set(jettyMetrics.getThreads()); + idleThreads.set(jettyMetrics.getIdleThreads()); + busyThreads.set(jettyMetrics.getBusyThreads()); + reservedThreads.set(jettyMetrics.getReservedThreads()); + queueSize.set(jettyMetrics.getQueueSize()); + lowOnThreads.set(jettyMetrics.isLowOnThreads()); + }); + } +} diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyModule.java b/java/com/google/gerrit/pgm/http/jetty/JettyModule.java index c818276a5d..32a8b6d0f2 100644 --- a/java/com/google/gerrit/pgm/http/jetty/JettyModule.java +++ b/java/com/google/gerrit/pgm/http/jetty/JettyModule.java @@ -31,5 +31,6 @@ public class JettyModule extends LifecycleModule { bind(JettyServer.class); listener().to(JettyServer.Lifecycle.class); install(new FactoryModuleBuilder().build(HttpLogFactory.class)); + bind(JettyMetrics.class); } } diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index 25a28a4cfd..8c7958c8c5 100644 --- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java @@ -67,7 +67,6 @@ import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jgit.lib.Config; @Singleton @@ -116,9 +115,49 @@ public class JettyServer { } } + static class Metrics { + private final QueuedThreadPool threadPool; + + Metrics(QueuedThreadPool threadPool) { + this.threadPool = threadPool; + } + + public int getIdleThreads() { + return threadPool.getIdleThreads(); + } + + public int getBusyThreads() { + return threadPool.getBusyThreads(); + } + + public int getReservedThreads() { + return threadPool.getReservedThreads(); + } + + public int getMinThreads() { + return threadPool.getMinThreads(); + } + + public int getMaxThreads() { + return threadPool.getMaxThreads(); + } + + public int getThreads() { + return threadPool.getThreads(); + } + + public int getQueueSize() { + return threadPool.getQueueSize(); + } + + public boolean isLowOnThreads() { + return threadPool.isLowOnThreads(); + } + } + private final SitePaths site; private final Server httpd; - + private final Metrics metrics; private boolean reverseProxy; @Inject @@ -130,8 +169,10 @@ public class JettyServer { HttpLogFactory httpLogFactory) { this.site = site; - httpd = new Server(threadPool(cfg, threadSettingsConfig)); + QueuedThreadPool pool = threadPool(cfg, threadSettingsConfig); + httpd = new Server(pool); httpd.setConnectors(listen(httpd, cfg)); + metrics = new Metrics(pool); Handler app = makeContext(env, cfg); if (cfg.getBoolean("httpd", "requestLog", !reverseProxy)) { @@ -160,6 +201,10 @@ public class JettyServer { httpd.setStopAtShutdown(false); } + Metrics getMetrics() { + return metrics; + } + private Connector[] listen(Server server, Config cfg) { // OpenID and certain web-based single-sign-on products can cause // some very long headers, especially in the Referer header. We @@ -339,7 +384,7 @@ public class JettyServer { return site.resolve(path); } - private ThreadPool threadPool(Config cfg, ThreadSettingsConfig threadSettingsConfig) { + private QueuedThreadPool threadPool(Config cfg, ThreadSettingsConfig threadSettingsConfig) { int maxThreads = threadSettingsConfig.getHttpdMaxThreads(); int minThreads = cfg.getInt("httpd", null, "minthreads", 5); int maxQueued = cfg.getInt("httpd", null, "maxqueued", 200);