Repository: zookeeper Updated Branches: refs/heads/master 4ad2341c1 -> ef8b5ab26
ZOOKEEPER-3123: MetricsProvider Lifecycle in ZooKeeper Server Manage the lifecycle of a MetricsProvider inside a ZooKeeper server. - handle configuration - start and configure the MetricsProvider - notify shutdown to the MetricsProvider This is an early preview, because there are some points to discuss: - We have to throw an IOException in case of failure (in order not to change the current signature of main methods used to start the server) - The patch only provides the lifecycle, it introduces some dead fields (root metrics context), this is expected as the real instrumentation will be done in a further step, is it okay ? - Test cases cover only standalone mode, do we need to add a new suite for testing configuration and boot errors on QuorumPeer mode ? (the answer should be YES) - MetricsProvider configuration is not subject to dynamic 'reconfig' Configuration to the MetricsProvider is not yet handled, the idea is to let the user configure properties like metricsProvider.className=o.a.z.metrics.prometheus.PrometheusMetricsProvider metricsProvider.customParam1=value1 metricsProvider.customParam2=value2 in this case the MetricsProvider will receive {customParam1=value1, customParam2=value2} as parameter in configure() is it okay ? Author: Enrico Olivelli <eolive...@apache.org> Reviewers: fang...@apache.org, an...@apache.org Closes #601 from eolivelli/fix/boot-provider and squashes the following commits: 8964ed17 [Enrico Olivelli] Fix tests, use getters in order to support Mock QuorumPeerConfig 93749f6a [Enrico Olivelli] fix imports 7ad552db [Enrico Olivelli] Add testcases around QuorumPeerMain 22f79eb8 [Enrico Olivelli] clean up c92450e4 [Enrico Olivelli] implement MetricsProvider configuration, fix some review comments f4f66ecb [Enrico Olivelli] ZOOKEEPER-3123 MetricsProvider Lifecycle in ZooKeeper Server Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/ef8b5ab2 Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/ef8b5ab2 Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/ef8b5ab2 Branch: refs/heads/master Commit: ef8b5ab263270e41504dddc5fcc8c6b3419e5b4b Parents: 4ad2341 Author: Enrico Olivelli <eolive...@apache.org> Authored: Tue Sep 11 15:23:32 2018 +0200 Committer: Andor Molnar <an...@apache.org> Committed: Tue Sep 11 15:23:32 2018 +0200 ---------------------------------------------------------------------- .../metrics/impl/MetricsProviderBootstrap.java | 50 +++ .../metrics/impl/NullMetricsProvider.java | 100 +++++ .../apache/zookeeper/server/ServerConfig.java | 9 + .../zookeeper/server/ZooKeeperServer.java | 11 + .../zookeeper/server/ZooKeeperServerMain.java | 23 +- .../zookeeper/server/quorum/QuorumPeer.java | 8 + .../server/quorum/QuorumPeerConfig.java | 20 +- .../zookeeper/server/quorum/QuorumPeerMain.java | 23 ++ .../metrics/BaseTestMetricsProvider.java | 137 +++++++ .../server/ZooKeeperServerMainTest.java | 197 ++++++++++ .../server/quorum/QuorumPeerMainTest.java | 375 +++++++++++++++++++ 11 files changed, 950 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/metrics/impl/MetricsProviderBootstrap.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/metrics/impl/MetricsProviderBootstrap.java b/src/java/main/org/apache/zookeeper/metrics/impl/MetricsProviderBootstrap.java new file mode 100644 index 0000000..85716b2 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/metrics/impl/MetricsProviderBootstrap.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.zookeeper.metrics.impl; + +import java.util.Properties; +import org.apache.zookeeper.metrics.MetricsProvider; +import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility for bootstrap process of MetricsProviders + */ +public abstract class MetricsProviderBootstrap { + + private static final Logger LOG = LoggerFactory.getLogger(MetricsProviderBootstrap.class); + + public static MetricsProvider startMetricsProvider(String metricsProviderClassName, Properties configuration) + throws MetricsProviderLifeCycleException { + try { + MetricsProvider metricsProvider = (MetricsProvider) Class.forName(metricsProviderClassName, + true, Thread.currentThread().getContextClassLoader()).newInstance(); + metricsProvider.configure(configuration); + metricsProvider.start(); + return metricsProvider; + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException error) { + LOG.error("Cannot boot MetricsProvider {}", metricsProviderClassName, error); + throw new MetricsProviderLifeCycleException("Cannot boot MetricsProvider " + metricsProviderClassName, + error); + } catch (MetricsProviderLifeCycleException error) { + LOG.error("Cannot boot MetricsProvider {}", metricsProviderClassName, error); + throw error; + } + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/metrics/impl/NullMetricsProvider.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/metrics/impl/NullMetricsProvider.java b/src/java/main/org/apache/zookeeper/metrics/impl/NullMetricsProvider.java new file mode 100644 index 0000000..8b22557 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/metrics/impl/NullMetricsProvider.java @@ -0,0 +1,100 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.zookeeper.metrics.impl; + +import java.util.Properties; +import org.apache.zookeeper.metrics.Counter; +import org.apache.zookeeper.metrics.Gauge; +import org.apache.zookeeper.metrics.MetricsContext; +import org.apache.zookeeper.metrics.MetricsProvider; +import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; +import org.apache.zookeeper.metrics.Summary; + +/** + * This is a dummy MetricsProvider which does nothing. + */ +public class NullMetricsProvider implements MetricsProvider { + + @Override + public void configure(Properties configuration) throws MetricsProviderLifeCycleException { + } + + @Override + public void start() throws MetricsProviderLifeCycleException { + } + + @Override + public MetricsContext getRootContext() { + return NullMetricsContext.INSTANCE; + } + + @Override + public void stop() { + } + + public static final class NullMetricsContext implements MetricsContext { + + public static final NullMetricsContext INSTANCE = new NullMetricsContext(); + + @Override + public MetricsContext getContext(String name) { + return INSTANCE; + } + + @Override + public Counter getCounter(String name) { + return NullCounter.INSTANCE; + } + + @Override + public boolean registerGauge(String name, Gauge gauge) { + return true; + } + + @Override + public Summary getSummary(String name) { + return NullSummary.INSTANCE; + } + + } + + private static final class NullCounter implements Counter { + + private static final NullCounter INSTANCE = new NullCounter(); + + @Override + public void inc(long delta) { + } + + @Override + public long get() { + return 0; + } + + } + + private static final class NullSummary implements Summary { + + private static final NullSummary INSTANCE = new NullSummary(); + + @Override + public void registerValue(long value) { + } + + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/server/ServerConfig.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/ServerConfig.java b/src/java/main/org/apache/zookeeper/server/ServerConfig.java index dd3f1da..a6b0760 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/ServerConfig.java @@ -21,8 +21,10 @@ package org.apache.zookeeper.server; import java.io.File; import java.net.InetSocketAddress; import java.util.Arrays; +import java.util.Properties; import org.apache.yetus.audience.InterfaceAudience; +import org.apache.zookeeper.metrics.impl.NullMetricsProvider; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; @@ -48,6 +50,8 @@ public class ServerConfig { protected int minSessionTimeout = -1; /** defaults to -1 if not set explicitly */ protected int maxSessionTimeout = -1; + protected String metricsProviderClassName = NullMetricsProvider.class.getName(); + protected Properties metricsProviderConfiguration = new Properties(); /** * Parse arguments for server configuration @@ -99,6 +103,8 @@ public class ServerConfig { maxClientCnxns = config.getMaxClientCnxns(); minSessionTimeout = config.getMinSessionTimeout(); maxSessionTimeout = config.getMaxSessionTimeout(); + metricsProviderClassName = config.getMetricsProviderClassName(); + metricsProviderConfiguration = config.getMetricsProviderConfiguration(); } public InetSocketAddress getClientPortAddress() { @@ -115,4 +121,7 @@ public class ServerConfig { public int getMinSessionTimeout() { return minSessionTimeout; } /** maximum session timeout in milliseconds, -1 if unset */ public int getMaxSessionTimeout() { return maxSessionTimeout; } + public String getMetricsProviderClassName() { return metricsProviderClassName; } + public Properties getMetricsProviderConfiguration() { return metricsProviderConfiguration; } + } http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index 2c9e5e7..09c6a8a 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -52,6 +52,8 @@ import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.jmx.MBeanRegistry; +import org.apache.zookeeper.metrics.MetricsContext; +import org.apache.zookeeper.metrics.impl.NullMetricsProvider; import org.apache.zookeeper.proto.AuthPacket; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ConnectResponse; @@ -127,6 +129,7 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider { private final ServerStats serverStats; private final ZooKeeperServerListener listener; + private MetricsContext rootMetricsContext = NullMetricsProvider.NullMetricsContext.INSTANCE; private ZooKeeperServerShutdownHandler zkShutdownHandler; private volatile int createSessionTrackerServerId = 1; @@ -877,6 +880,14 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider { secureServerCnxnFactory = factory; } + public MetricsContext getRootMetricsContext() { + return rootMetricsContext; + } + + public void setRootMetricsContext(MetricsContext rootMetricsContext) { + this.rootMetricsContext = rootMetricsContext; + } + /** * return the last proceesed id from the * datatree http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java index 0929696..2af96fd 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java @@ -19,6 +19,7 @@ package org.apache.zookeeper.server; import java.io.IOException; +import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -26,6 +27,9 @@ import javax.management.JMException; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.jmx.ManagedUtil; +import org.apache.zookeeper.metrics.MetricsProvider; +import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; +import org.apache.zookeeper.metrics.impl.MetricsProviderBootstrap; import org.apache.zookeeper.server.admin.AdminServer; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.admin.AdminServerFactory; @@ -50,7 +54,7 @@ public class ZooKeeperServerMain { private ServerCnxnFactory cnxnFactory; private ServerCnxnFactory secureCnxnFactory; private ContainerManager containerManager; - + private MetricsProvider metricsProvider; private AdminServer adminServer; /* @@ -117,6 +121,15 @@ public class ZooKeeperServerMain { LOG.info("Starting server"); FileTxnSnapLog txnLog = null; try { + try { + metricsProvider = MetricsProviderBootstrap + .startMetricsProvider(config.getMetricsProviderClassName(), + config.getMetricsProviderConfiguration()); + } catch (MetricsProviderLifeCycleException error) { + throw new IOException("Cannot boot MetricsProvider "+config.getMetricsProviderClassName(), + error); + } + // Note that this thread isn't going to be doing anything else, // so rather than spawning another thread, we will just call // run() in this thread. @@ -124,6 +137,7 @@ public class ZooKeeperServerMain { txnLog = new FileTxnSnapLog(config.dataLogDir, config.dataDir); final ZooKeeperServer zkServer = new ZooKeeperServer(txnLog, config.tickTime, config.minSessionTimeout, config.maxSessionTimeout, null); + zkServer.setRootMetricsContext(metricsProvider.getRootContext()); txnLog.setServerStats(zkServer.serverStats()); // Registers shutdown handler which will be used to know the @@ -179,6 +193,13 @@ public class ZooKeeperServerMain { if (txnLog != null) { txnLog.close(); } + if (metricsProvider != null) { + try { + metricsProvider.stop(); + } catch (Throwable error) { + LOG.warn("Error while stopping metrics", error); + } + } } } http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java index 1a5acdd..bb9e0d1 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -74,6 +74,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; +import org.apache.zookeeper.metrics.MetricsContext; +import org.apache.zookeeper.metrics.impl.NullMetricsProvider; /** * This class manages the quorum protocol. There are three states this server @@ -773,6 +775,8 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider AdminServer adminServer; + private MetricsContext rootMetricsContext = NullMetricsProvider.NullMetricsContext.INSTANCE; + public static QuorumPeer testingQuorumPeer() throws SaslException { return new QuorumPeer(); } @@ -1679,6 +1683,10 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider this.secureCnxnFactory = secureCnxnFactory; } + public void setRootMetricsContext(MetricsContext rootMetricsContext) { + this.rootMetricsContext = rootMetricsContext; + } + private void startServerCnxnFactory() { if (cnxnFactory != null) { cnxnFactory.start(); http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java index ab7b9f6..19558cf 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java @@ -54,9 +54,9 @@ import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.VerifyingFileFactory; -import org.apache.zookeeper.server.util.ConfigUtils; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; +import org.apache.zookeeper.metrics.impl.NullMetricsProvider; @InterfaceAudience.Public public class QuorumPeerConfig { @@ -79,6 +79,8 @@ public class QuorumPeerConfig { protected int minSessionTimeout = -1; /** defaults to -1 if not set explicitly */ protected int maxSessionTimeout = -1; + protected String metricsProviderClassName = NullMetricsProvider.class.getName(); + protected Properties metricsProviderConfiguration = new Properties(); protected boolean localSessionsEnabled = false; protected boolean localSessionsUpgradingEnabled = false; @@ -325,6 +327,11 @@ public class QuorumPeerConfig { quorumServicePrincipal = value; } else if (key.equals("quorum.cnxn.threads.size")) { quorumCnxnThreadsSize = Integer.parseInt(value); + } else if (key.equals("metricsProvider.className")) { + metricsProviderClassName = value; + } else if (key.startsWith("metricsProvider.")) { + String keyForMetricsProvider = key.substring(16); + metricsProviderConfiguration.put(keyForMetricsProvider, value); } else { System.setProperty("zookeeper." + key, value); } @@ -409,7 +416,14 @@ public class QuorumPeerConfig { if (minSessionTimeout > maxSessionTimeout) { throw new IllegalArgumentException( "minSessionTimeout must not be larger than maxSessionTimeout"); - } + } + + LOG.info("metricsProvider.className is {}", metricsProviderClassName); + try { + Class.forName(metricsProviderClassName, false, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException error) { + throw new IllegalArgumentException("metrics provider class was not found", error); + } // backward compatibility - dynamic configuration in the same file as // static configuration params see writeDynamicConfig() @@ -735,6 +749,8 @@ public class QuorumPeerConfig { public int getMaxClientCnxns() { return maxClientCnxns; } public int getMinSessionTimeout() { return minSessionTimeout; } public int getMaxSessionTimeout() { return maxSessionTimeout; } + public String getMetricsProviderClassName() { return metricsProviderClassName; } + public Properties getMetricsProviderConfiguration() { return metricsProviderConfiguration; } public boolean areLocalSessionsEnabled() { return localSessionsEnabled; } public boolean isLocalSessionsUpgradingEnabled() { return localSessionsUpgradingEnabled; http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java ---------------------------------------------------------------------- diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java index 2f2bdde..11b5c0b 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -18,6 +18,7 @@ package org.apache.zookeeper.server.quorum; import java.io.IOException; +import java.util.Properties; import javax.management.JMException; import javax.security.sasl.SaslException; @@ -26,6 +27,9 @@ import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.ManagedUtil; +import org.apache.zookeeper.metrics.MetricsProvider; +import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; +import org.apache.zookeeper.metrics.impl.MetricsProviderBootstrap; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; @@ -140,7 +144,17 @@ public class QuorumPeerMain { } LOG.info("Starting quorum peer"); + MetricsProvider metricsProvider; try { + metricsProvider = MetricsProviderBootstrap + .startMetricsProvider(config.getMetricsProviderClassName(), + config.getMetricsProviderConfiguration()); + } catch (MetricsProviderLifeCycleException error) { + throw new IOException("Cannot boot MetricsProvider " + config.getMetricsProviderClassName(), + error); + } + try { + ServerCnxnFactory cnxnFactory = null; ServerCnxnFactory secureCnxnFactory = null; @@ -159,6 +173,7 @@ public class QuorumPeerMain { } quorumPeer = getQuorumPeer(); + quorumPeer.setRootMetricsContext(metricsProvider.getRootContext()); quorumPeer.setTxnFactory(new FileTxnSnapLog( config.getDataLogDir(), config.getDataDir())); @@ -203,6 +218,14 @@ public class QuorumPeerMain { } catch (InterruptedException e) { // warn, but generally this is ok LOG.warn("Quorum Peer interrupted", e); + } finally { + if (metricsProvider != null) { + try { + metricsProvider.stop(); + } catch (Throwable error) { + LOG.warn("Error while stopping metrics", error); + } + } } } http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/test/org/apache/zookeeper/metrics/BaseTestMetricsProvider.java ---------------------------------------------------------------------- diff --git a/src/java/test/org/apache/zookeeper/metrics/BaseTestMetricsProvider.java b/src/java/test/org/apache/zookeeper/metrics/BaseTestMetricsProvider.java new file mode 100644 index 0000000..d50b547 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/metrics/BaseTestMetricsProvider.java @@ -0,0 +1,137 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.zookeeper.metrics; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.zookeeper.metrics.impl.NullMetricsProvider; + +/** + * Simple MetricsProvider for tests. + */ +public abstract class BaseTestMetricsProvider implements MetricsProvider { + + @Override + public void configure(Properties prprts) throws MetricsProviderLifeCycleException { + } + + @Override + public void start() throws MetricsProviderLifeCycleException { + } + + @Override + public MetricsContext getRootContext() { + return NullMetricsProvider.NullMetricsContext.INSTANCE; + } + + @Override + public void stop() { + } + + public static final class MetricsProviderCapturingLifecycle extends BaseTestMetricsProvider { + + public static final AtomicBoolean configureCalled = new AtomicBoolean(); + public static final AtomicBoolean startCalled = new AtomicBoolean(); + public static final AtomicBoolean stopCalled = new AtomicBoolean(); + public static final AtomicBoolean getRootContextCalled = new AtomicBoolean(); + + public static void reset() { + configureCalled.set(false); + startCalled.set(false); + stopCalled.set(false); + getRootContextCalled.set(false); + } + + @Override + public void configure(Properties prprts) throws MetricsProviderLifeCycleException { + if (!configureCalled.compareAndSet(false, true)) { + // called twice + throw new IllegalStateException(); + } + } + + @Override + public void start() throws MetricsProviderLifeCycleException { + if (!startCalled.compareAndSet(false, true)) { + // called twice + throw new IllegalStateException(); + } + } + + @Override + public MetricsContext getRootContext() { + if (!getRootContextCalled.compareAndSet(false, true)) { + // called twice + throw new IllegalStateException(); + } + return NullMetricsProvider.NullMetricsContext.INSTANCE; + } + + @Override + public void stop() { + if (!stopCalled.compareAndSet(false, true)) { + // called twice + throw new IllegalStateException(); + } + } + + } + + public static final class MetricsProviderWithErrorInStart extends BaseTestMetricsProvider { + + @Override + public void start() throws MetricsProviderLifeCycleException { + throw new MetricsProviderLifeCycleException(); + } + + } + + public static final class MetricsProviderWithErrorInConfigure extends BaseTestMetricsProvider { + + @Override + public void configure(Properties prprts) throws MetricsProviderLifeCycleException { + throw new MetricsProviderLifeCycleException(); + } + + } + + public static final class MetricsProviderWithConfiguration extends BaseTestMetricsProvider { + + public static final AtomicInteger httpPort = new AtomicInteger(); + + @Override + public void configure(Properties prprts) throws MetricsProviderLifeCycleException { + httpPort.set(Integer.parseInt(prprts.getProperty("httpPort"))); + } + + } + + public static final class MetricsProviderWithErrorInStop extends BaseTestMetricsProvider { + + public static final AtomicBoolean stopCalled = new AtomicBoolean(); + + @Override + public void stop() { + stopCalled.set(true); + throw new RuntimeException(); + } + + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java ---------------------------------------------------------------------- diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java index a217576..f124fcd 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java @@ -39,6 +39,12 @@ import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.common.PathUtils; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderCapturingLifecycle; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithConfiguration; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithErrorInConfigure; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithErrorInStart; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithErrorInStop; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.test.ClientBase; @@ -347,6 +353,197 @@ public class ZooKeeperServerMainTest extends ZKTestCase implements Watcher { } /** + * Test verifies that the server shouldn't boot with an invalid metrics provider + */ + @Test + public void testInvalidMetricsProvider() + throws Exception { + ClientBase.setupTestEnv(); + + final int CLIENT_PORT = PortAssignment.unique(); + final String configs = "metricsProvider.className=BadClass\n"; + MainThread main = new MainThread(CLIENT_PORT, true, configs); + String args[] = new String[1]; + args[0] = main.confFile.toString(); + try { + main.main.initializeAndRun(args); + Assert.fail("Must throw exception as metrics provider is not " + + "well configured"); + } catch (ConfigException iae) { + // expected + } + } + + /** + * Test verifies that the server shouldn't boot with a faulty metrics provider + */ + @Test + public void testFaultyMetricsProviderOnStart() + throws Exception { + ClientBase.setupTestEnv(); + + final int CLIENT_PORT = PortAssignment.unique(); + final String configs = "metricsProvider.className="+MetricsProviderWithErrorInStart.class.getName()+"\n"; + MainThread main = new MainThread(CLIENT_PORT, true, configs); + String args[] = new String[1]; + args[0] = main.confFile.toString(); + try { + main.main.initializeAndRun(args); + Assert.fail("Must throw exception as metrics provider cannot boot"); + } catch (IOException iae) { + // expected + } + } + + /** + * Test verifies that the server shouldn't boot with a faulty metrics provider + */ + @Test + public void testFaultyMetricsProviderOnConfigure() + throws Exception { + ClientBase.setupTestEnv(); + + final int CLIENT_PORT = PortAssignment.unique(); + final String configs = "metricsProvider.className="+MetricsProviderWithErrorInConfigure.class.getName()+"\n"; + MainThread main = new MainThread(CLIENT_PORT, true, configs); + String args[] = new String[1]; + args[0] = main.confFile.toString(); + try { + main.main.initializeAndRun(args); + Assert.fail("Must throw exception as metrics provider is cannot boot"); + } catch (IOException iae) { + // expected + } + } + + /** + * Test verifies that the server shouldn't be affected but runtime errors on stop() + */ + @Test + public void testFaultyMetricsProviderOnStop() + throws Exception { + ClientBase.setupTestEnv(); + + final int CLIENT_PORT = PortAssignment.unique(); + MetricsProviderWithErrorInStop.stopCalled.set(false); + final String configs = "metricsProvider.className="+MetricsProviderWithErrorInStop.class.getName()+"\n"; + MainThread main = new MainThread(CLIENT_PORT, true, configs); + main.start(); + + Assert.assertTrue("waiting for server being up", + ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT)); + + clientConnected = new CountDownLatch(1); + ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT, this); + Assert.assertTrue("Failed to establish zkclient connection!", + clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)); + + zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Assert.assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); + zk.close(); + + main.shutdown(); + main.join(); + main.deleteDirs(); + + Assert.assertTrue("waiting for server down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT)); + Assert.assertTrue(MetricsProviderWithErrorInStop.stopCalled.get()); + } + + /** + * Test verifies that configuration is passed to the MetricsProvider. + */ + @Test + public void testMetricsProviderConfiguration() + throws Exception { + ClientBase.setupTestEnv(); + + final int CLIENT_PORT = PortAssignment.unique(); + MetricsProviderWithConfiguration.httpPort.set(0); + final String configs = "metricsProvider.className="+MetricsProviderWithConfiguration.class.getName()+"\n"+ + "metricsProvider.httpPort=1234\n"; + MainThread main = new MainThread(CLIENT_PORT, true, configs); + main.start(); + + Assert.assertTrue("waiting for server being up", + ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT)); + + clientConnected = new CountDownLatch(1); + ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT, this); + Assert.assertTrue("Failed to establish zkclient connection!", + clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)); + + zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Assert.assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); + zk.close(); + + main.shutdown(); + main.join(); + main.deleteDirs(); + + Assert.assertTrue("waiting for server down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT)); + Assert.assertEquals(1234, MetricsProviderWithConfiguration.httpPort.get()); + } + + /** + * Test verifies that all of the lifecycle methods of the MetricsProvider are called. + */ + @Test + public void testMetricsProviderLifecycle() + throws Exception { + ClientBase.setupTestEnv(); + MetricsProviderCapturingLifecycle.reset(); + + final int CLIENT_PORT = PortAssignment.unique(); + final String configs = "metricsProvider.className="+MetricsProviderCapturingLifecycle.class.getName()+"\n"+ + "metricsProvider.httpPort=1234\n"; + MainThread main = new MainThread(CLIENT_PORT, true, configs); + main.start(); + + Assert.assertTrue("waiting for server being up", + ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT)); + + clientConnected = new CountDownLatch(1); + ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT, this); + Assert.assertTrue("Failed to establish zkclient connection!", + clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)); + + zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Assert.assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); + zk.close(); + + main.shutdown(); + main.join(); + main.deleteDirs(); + + Assert.assertTrue("waiting for server down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT)); + + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.configureCalled.get()); + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.startCalled.get()); + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.getRootContextCalled.get()); + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.stopCalled.get()); + } + + /** * Test verifies that the server is able to redefine if user configured only * minSessionTimeout limit */ http://git-wip-us.apache.org/repos/asf/zookeeper/blob/ef8b5ab2/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java ---------------------------------------------------------------------- diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java index d48ea04..e613a09 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -58,6 +58,8 @@ import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.metrics.BaseTestMetricsProvider; +import org.apache.zookeeper.metrics.impl.NullMetricsProvider; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.test.ClientBase; @@ -1146,6 +1148,8 @@ public class QuorumPeerMainTest extends QuorumPeerTestBase { QuorumPeerConfig configMock = mock(QuorumPeerConfig.class); when(configMock.getDataDir()).thenReturn(dataDir); when(configMock.getDataLogDir()).thenReturn(dataLogDir); + when(configMock.getMetricsProviderClassName()) + .thenReturn(NullMetricsProvider.class.getName()); QuorumPeer qpMock = mock(QuorumPeer.class); @@ -1486,6 +1490,377 @@ public class QuorumPeerMainTest extends QuorumPeerTestBase { } } + /** + * Verify boot works configuring a MetricsProvider + */ + @Test + public void testMetricsProviderLifecycle() throws Exception { + ClientBase.setupTestEnv(); + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.reset(); + + // setup the logger to capture all logs + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = getConsoleAppender(os, Level.WARN); + Logger qlogger = Logger.getLogger("org.apache.zookeeper.server.quorum"); + qlogger.addAppender(appender); + + try { + final int CLIENT_PORT_QP1 = PortAssignment.unique(); + final int CLIENT_PORT_QP2 = PortAssignment.unique(); + + String quorumCfgSectionServer + = "server.1=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "server.2=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\n"; + + // server 1 boots with a MetricsProvider + String quorumCfgSectionServer1 = + quorumCfgSectionServer + + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.class.getName() + "\n"; + + MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSectionServer1); + MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSectionServer); + q1.start(); + q2.start(); + + boolean isup1 + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, + 30000); + boolean isup2 + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, + 30000); + Assert.assertTrue("Server 1 never came up", isup1); + Assert.assertTrue("Server 2 never came up", isup2); + + q1.shutdown(); + q2.shutdown(); + + Assert.assertTrue("waiting for server 1 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, + ClientBase.CONNECTION_TIMEOUT)); + + Assert.assertTrue("waiting for server 2 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, + ClientBase.CONNECTION_TIMEOUT)); + } finally { + qlogger.removeAppender(appender); + } + + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.configureCalled.get()); + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.startCalled.get()); + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.getRootContextCalled.get()); + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.stopCalled.get()); + } + + /** + * Test verifies that configuration is passed to the MetricsProvider. + */ + @Test + public void testMetricsProviderConfiguration() throws Exception { + ClientBase.setupTestEnv(); + BaseTestMetricsProvider.MetricsProviderWithConfiguration.httpPort.set(0); + + // setup the logger to capture all logs + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = getConsoleAppender(os, Level.WARN); + Logger qlogger = Logger.getLogger("org.apache.zookeeper.server.quorum"); + qlogger.addAppender(appender); + + try { + final int CLIENT_PORT_QP1 = PortAssignment.unique(); + final int CLIENT_PORT_QP2 = PortAssignment.unique(); + + String quorumCfgSectionServer + = "server.1=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "server.2=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\n"; + + // server 1 boots with a MetricsProvider + String quorumCfgSectionServer1 = + quorumCfgSectionServer + + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithConfiguration.class.getName() + "\n" + + "metricsProvider.httpPort=1234"; + + MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSectionServer1); + MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSectionServer); + q1.start(); + q2.start(); + + boolean isup1 + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, + 30000); + boolean isup2 + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, + 30000); + Assert.assertTrue("Server 1 never came up", isup1); + Assert.assertTrue("Server 2 never came up", isup2); + + q1.shutdown(); + q2.shutdown(); + + Assert.assertTrue("waiting for server 1 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, + ClientBase.CONNECTION_TIMEOUT)); + + Assert.assertTrue("waiting for server 2 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, + ClientBase.CONNECTION_TIMEOUT)); + } finally { + qlogger.removeAppender(appender); + } + + Assert.assertEquals(1234, + BaseTestMetricsProvider.MetricsProviderWithConfiguration.httpPort.get()); + } + + /** + * Test verifies that the server shouldn't be affected but runtime errors on stop() + */ + @Test + public void testFaultyMetricsProviderOnStop() throws Exception { + ClientBase.setupTestEnv(); + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.reset(); + + // setup the logger to capture all logs + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = getConsoleAppender(os, Level.WARN); + Logger qlogger = Logger.getLogger("org.apache.zookeeper.server.quorum"); + qlogger.addAppender(appender); + + try { + final int CLIENT_PORT_QP1 = PortAssignment.unique(); + final int CLIENT_PORT_QP2 = PortAssignment.unique(); + + String quorumCfgSectionServer + = "server.1=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "server.2=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\n"; + + // server 1 boots with a MetricsProvider + String quorumCfgSectionServer1 = + quorumCfgSectionServer + + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithErrorInStop.class.getName() + "\n"; + + MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSectionServer1); + MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSectionServer); + q1.start(); + q2.start(); + + boolean isup1 + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, + 30000); + boolean isup2 + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, + 30000); + Assert.assertTrue("Server 1 never came up", isup1); + Assert.assertTrue("Server 2 never came up", isup2); + + q1.shutdown(); + q2.shutdown(); + + Assert.assertTrue("waiting for server 1 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, + ClientBase.CONNECTION_TIMEOUT)); + + Assert.assertTrue("waiting for server 2 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, + ClientBase.CONNECTION_TIMEOUT)); + } finally { + qlogger.removeAppender(appender); + } + + Assert.assertTrue("metrics provider lifecycle error", + BaseTestMetricsProvider.MetricsProviderWithErrorInStop.stopCalled.get()); + + LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); + String line; + boolean found = false; + Pattern p + = Pattern.compile(".*Error while stopping metrics.*"); + while ((line = r.readLine()) != null) { + found = p.matcher(line).matches(); + if (found) { + break; + } + } + Assert.assertTrue("complains about metrics provider", found); + } + + /** + * Verify boot fails with a bad MetricsProvider + */ + @Test + public void testInvalidMetricsProvider() throws Exception { + ClientBase.setupTestEnv(); + + // setup the logger to capture all logs + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = getConsoleAppender(os, Level.WARN); + Logger qlogger = Logger.getLogger("org.apache.zookeeper.server.quorum"); + qlogger.addAppender(appender); + + try { + final int CLIENT_PORT_QP1 = PortAssignment.unique(); + + String quorumCfgSection + = "server.1=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "server.2=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "metricsProvider.className=BadClass\n"; + + MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); + q1.start(); + + boolean isup + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, + 5000); + + Assert.assertFalse("Server never came up", isup); + + q1.shutdown(); + + Assert.assertTrue("waiting for server 1 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, + ClientBase.CONNECTION_TIMEOUT)); + + } finally { + qlogger.removeAppender(appender); + } + + LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); + String line; + boolean found = false; + Pattern p + = Pattern.compile(".*BadClass.*"); + while ((line = r.readLine()) != null) { + found = p.matcher(line).matches(); + if (found) { + break; + } + } + Assert.assertTrue("complains about metrics provider", found); + } + + /** + * Verify boot fails with a MetricsProvider with fails to start + */ + @Test + public void testFaultyMetricsProviderOnStart() throws Exception { + ClientBase.setupTestEnv(); + + // setup the logger to capture all logs + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = getConsoleAppender(os, Level.WARN); + Logger qlogger = Logger.getLogger("org.apache.zookeeper.server.quorum"); + qlogger.addAppender(appender); + + try { + final int CLIENT_PORT_QP1 = PortAssignment.unique(); + + String quorumCfgSection + = "server.1=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "server.2=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithErrorInStart.class.getName() + "\n"; + + MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); + q1.start(); + + boolean isup + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, + 5000); + + Assert.assertFalse("Server never came up", isup); + + q1.shutdown(); + + Assert.assertTrue("waiting for server 1 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, + ClientBase.CONNECTION_TIMEOUT)); + + } finally { + qlogger.removeAppender(appender); + } + + LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); + String line; + boolean found = false; + Pattern p + = Pattern.compile(".*MetricsProviderLifeCycleException.*"); + while ((line = r.readLine()) != null) { + found = p.matcher(line).matches(); + if (found) { + break; + } + } + Assert.assertTrue("complains about metrics provider MetricsProviderLifeCycleException", found); + } + + /** + * Verify boot fails with a MetricsProvider with fails to start + */ + @Test + public void testFaultyMetricsProviderOnConfigure() throws Exception { + ClientBase.setupTestEnv(); + + // setup the logger to capture all logs + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = getConsoleAppender(os, Level.WARN); + Logger qlogger = Logger.getLogger("org.apache.zookeeper.server.quorum"); + qlogger.addAppender(appender); + + try { + final int CLIENT_PORT_QP1 = PortAssignment.unique(); + + String quorumCfgSection + = "server.1=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "server.2=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithErrorInConfigure.class.getName() + "\n"; + + MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); + q1.start(); + + boolean isup + = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, + 5000); + + Assert.assertFalse("Server never came up", isup); + + q1.shutdown(); + + Assert.assertTrue("waiting for server 1 down", + ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, + ClientBase.CONNECTION_TIMEOUT)); + + } finally { + qlogger.removeAppender(appender); + } + + LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); + String line; + boolean found = false; + Pattern p + = Pattern.compile(".*MetricsProviderLifeCycleException.*"); + while ((line = r.readLine()) != null) { + found = p.matcher(line).matches(); + if (found) { + break; + } + } + Assert.assertTrue("complains about metrics provider MetricsProviderLifeCycleException", found); + } + static class Context { boolean quitFollowing = false; boolean exitWhenAckNewLeader = false;