http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/resources/lib/jline-2.11.LICENSE.txt ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/resources/lib/jline-2.11.LICENSE.txt b/zookeeper-server/src/main/resources/lib/jline-2.11.LICENSE.txt new file mode 100644 index 0000000..4ac9522 --- /dev/null +++ b/zookeeper-server/src/main/resources/lib/jline-2.11.LICENSE.txt @@ -0,0 +1,35 @@ +Copyright (c) 2002-2012, the original author or authors. +All rights reserved. + +http://www.opensource.org/licenses/bsd-license.php + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with +the distribution. + +Neither the name of JLine nor the names of its contributors +may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. +
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/resources/lib/log4j-1.2.17.LICENSE.txt ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/resources/lib/log4j-1.2.17.LICENSE.txt b/zookeeper-server/src/main/resources/lib/log4j-1.2.17.LICENSE.txt new file mode 100644 index 0000000..6279e52 --- /dev/null +++ b/zookeeper-server/src/main/resources/lib/log4j-1.2.17.LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 1999-2005 The Apache Software Foundation + + 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. http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/main/resources/lib/slf4j-1.7.25.LICENSE.txt ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/resources/lib/slf4j-1.7.25.LICENSE.txt b/zookeeper-server/src/main/resources/lib/slf4j-1.7.25.LICENSE.txt new file mode 100644 index 0000000..a502dd9 --- /dev/null +++ b/zookeeper-server/src/main/resources/lib/slf4j-1.7.25.LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2004-2017 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketTest.java new file mode 100644 index 0000000..054e1ed --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketTest.java @@ -0,0 +1,53 @@ +/** + * 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; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.common.ZKConfig; +import org.junit.Test; + +public class ClientCnxnSocketTest { + + @Test + public void testWhenInvalidJuteMaxBufferIsConfiguredIOExceptionIsThrown() { + ZKClientConfig clientConfig = new ZKClientConfig(); + String value = "SomeInvalidInt"; + clientConfig.setProperty(ZKConfig.JUTE_MAXBUFFER, value); + // verify ClientCnxnSocketNIO creation + try { + new ClientCnxnSocketNIO(clientConfig); + fail("IOException is expected."); + } catch (IOException e) { + assertTrue(e.getMessage().contains(value)); + } + // verify ClientCnxnSocketNetty creation + try { + new ClientCnxnSocketNetty(clientConfig); + fail("IOException is expected."); + } catch (IOException e) { + assertTrue(e.getMessage().contains(value)); + } + + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/ClientReconnectTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientReconnectTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientReconnectTest.java new file mode 100644 index 0000000..566b915 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientReconnectTest.java @@ -0,0 +1,78 @@ +/** + * 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; + + +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.zookeeper.client.ZKClientConfig; +import org.apache.zookeeper.client.HostProvider; +import org.junit.Assert; +import org.junit.Test; + +public class ClientReconnectTest extends ZKTestCase { + private SocketChannel sc; + private CountDownLatch countDownLatch = new CountDownLatch(3); + + class MockCnxn extends ClientCnxnSocketNIO { + MockCnxn() throws IOException { + super(new ZKClientConfig()); + } + + @Override + void registerAndConnect(SocketChannel sock, InetSocketAddress addr) throws + IOException { + countDownLatch.countDown(); + throw new IOException("failed to register"); + } + + @Override + SocketChannel createSock() { + return sc; + } + } + + @Test + public void testClientReconnect() throws IOException, InterruptedException { + HostProvider hostProvider = mock(HostProvider.class); + when(hostProvider.size()).thenReturn(1); + InetSocketAddress inaddr = new InetSocketAddress("127.0.0.1", 1111); + when(hostProvider.next(anyLong())).thenReturn(inaddr); + ZooKeeper zk = mock(ZooKeeper.class); + when(zk.getClientConfig()).thenReturn(new ZKClientConfig()); + sc = SocketChannel.open(); + + ClientCnxnSocketNIO nioCnxn = new MockCnxn(); + ClientWatchManager watcher = mock(ClientWatchManager.class); + ClientCnxn clientCnxn = new ClientCnxn( + "tmp", hostProvider, 5000, + zk, watcher, nioCnxn, false); + clientCnxn.start(); + countDownLatch.await(5000, TimeUnit.MILLISECONDS); + Assert.assertTrue(countDownLatch.getCount() == 0); + clientCnxn.close(); + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java new file mode 100644 index 0000000..8fcc371 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java @@ -0,0 +1,157 @@ +/** + * 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; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.client.HostProvider; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.Assert; +import org.junit.Test; + +public class ClientRequestTimeoutTest extends QuorumPeerTestBase { + private static final int SERVER_COUNT = 3; + private boolean dropPacket = false; + private int dropPacketType = ZooDefs.OpCode.create; + + @Test(timeout = 120000) + public void testClientRequestTimeout() throws Exception { + int requestTimeOut = 15000; + System.setProperty("zookeeper.request.timeout", + Integer.toString(requestTimeOut)); + final int clientPorts[] = new int[SERVER_COUNT]; + StringBuilder sb = new StringBuilder(); + String server; + + for (int i = 0; i < SERVER_COUNT; i++) { + clientPorts[i] = PortAssignment.unique(); + server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + + clientPorts[i]; + sb.append(server + "\n"); + } + String currentQuorumCfgSection = sb.toString(); + MainThread mt[] = new MainThread[SERVER_COUNT]; + + for (int i = 0; i < SERVER_COUNT; i++) { + mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, + false); + mt[i].start(); + } + + // ensure server started + for (int i = 0; i < SERVER_COUNT; i++) { + Assert.assertTrue("waiting for server " + i + " being up", + ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], + CONNECTION_TIMEOUT)); + } + + CountdownWatcher watch1 = new CountdownWatcher(); + CustomZooKeeper zk = new CustomZooKeeper(getCxnString(clientPorts), + ClientBase.CONNECTION_TIMEOUT, watch1); + watch1.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + + String data = "originalData"; + // lets see one successful operation + zk.create("/clientHang1", data.getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + + // now make environment for client hang + dropPacket = true; + dropPacketType = ZooDefs.OpCode.create; + + // Test synchronous API + try { + zk.create("/clientHang2", data.getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + fail("KeeperException is expected."); + } catch (KeeperException exception) { + assertEquals(KeeperException.Code.REQUESTTIMEOUT.intValue(), + exception.code().intValue()); + } + + // do cleanup + zk.close(); + for (int i = 0; i < SERVER_COUNT; i++) { + mt[i].shutdown(); + } + } + + /** + * @return connection string in the form of + * 127.0.0.1:port1,127.0.0.1:port2,127.0.0.1:port3 + */ + private String getCxnString(int[] clientPorts) { + StringBuffer hostPortBuffer = new StringBuffer(); + for (int i = 0; i < clientPorts.length; i++) { + hostPortBuffer.append("127.0.0.1:"); + hostPortBuffer.append(clientPorts[i]); + if (i != (clientPorts.length - 1)) { + hostPortBuffer.append(','); + } + } + return hostPortBuffer.toString(); + } + + class CustomClientCnxn extends ClientCnxn { + + public CustomClientCnxn(String chrootPath, HostProvider hostProvider, + int sessionTimeout, ZooKeeper zooKeeper, + ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket, + boolean canBeReadOnly) throws IOException { + super(chrootPath, hostProvider, sessionTimeout, zooKeeper, watcher, + clientCnxnSocket, canBeReadOnly); + } + + @Override + public void finishPacket(Packet p) { + if (dropPacket && p.requestHeader.getType() == dropPacketType) { + // do nothing, just return, it is the same as packet is dropped + // by the network + return; + } + super.finishPacket(p); + } + } + + class CustomZooKeeper extends ZooKeeper { + public CustomZooKeeper(String connectString, int sessionTimeout, + Watcher watcher) throws IOException { + super(connectString, sessionTimeout, watcher); + } + + @Override + protected ClientCnxn createConnection(String chrootPath, + HostProvider hostProvider, int sessionTimeout, + ZooKeeper zooKeeper, ClientWatchManager watcher, + ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly) + throws IOException { + return new CustomClientCnxn(chrootPath, hostProvider, + sessionTimeout, zooKeeper, watcher, clientCnxnSocket, + canBeReadOnly); + } + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/CustomHostProviderTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/CustomHostProviderTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/CustomHostProviderTest.java new file mode 100644 index 0000000..f9762d2 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/CustomHostProviderTest.java @@ -0,0 +1,79 @@ +/** + * 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; + +import org.apache.zookeeper.client.HostProvider; +import org.apache.zookeeper.test.ClientBase; +import org.junit.Assert; +import org.junit.Test; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; + +public class CustomHostProviderTest extends ZKTestCase implements Watcher { + private AtomicInteger counter = new AtomicInteger(3); + + private class SpecialHostProvider implements HostProvider { + // ignores its connectstring, and next() always returns localhost:2181 + // it will count down when updateServerList() is called + @Override + public int size() { + return 1; + } + @Override + public InetSocketAddress next(long spinDelay) { + return new InetSocketAddress("127.0.0.1", 2181); + } + @Override + public void onConnected() { + } + @Override + public boolean updateServerList(Collection<InetSocketAddress> + serverAddresses, InetSocketAddress currentHost) { + counter.decrementAndGet(); + return false; + } + } + @Override + public void process(WatchedEvent event) { + } + + @Test + public void testZooKeeperWithCustomHostProvider() throws IOException, + InterruptedException { + final int CLIENT_PORT = PortAssignment.unique(); + final HostProvider specialHostProvider = new SpecialHostProvider(); + int expectedCounter = 3; + counter.set(expectedCounter); + + ZooKeeper zkDefaults = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT, this, false); + + ZooKeeper zkSpecial = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, + ClientBase.CONNECTION_TIMEOUT, this, false, specialHostProvider); + + Assert.assertTrue(counter.get() == expectedCounter); + zkDefaults.updateServerList("127.0.0.1:" + PortAssignment.unique()); + Assert.assertTrue(counter.get() == expectedCounter); + + zkSpecial.updateServerList("127.0.0.1:" + PortAssignment.unique()); + expectedCounter--; + Assert.assertTrue(counter.get() == expectedCounter); + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java b/zookeeper-server/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java new file mode 100644 index 0000000..3456a15 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java @@ -0,0 +1,110 @@ +/** + * 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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.junit.Test; +import org.junit.internal.runners.statements.InvokeMethod; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import java.util.Arrays; +import java.util.List; + +/** + * The sole responsibility of this class is to print to the log when a test + * starts and when it finishes. + */ +public class JUnit4ZKTestRunner extends BlockJUnit4ClassRunner { + private static final Logger LOG = LoggerFactory.getLogger(JUnit4ZKTestRunner.class); + + public JUnit4ZKTestRunner(Class<?> klass) throws InitializationError { + super(klass); + } + + public static List<FrameworkMethod> computeTestMethodsForClass(final Class klass, final List<FrameworkMethod> defaultMethods) { + List<FrameworkMethod> list = defaultMethods; + String methodName = System.getProperty("test.method"); + if (methodName == null) { + LOG.info("No test.method specified. using default methods."); + } else { + LOG.info("Picked up test.method={}", methodName); + try { + list = Arrays.asList(new FrameworkMethod(klass.getMethod(methodName))); + } catch (NoSuchMethodException nsme) { + LOG.warn("{} does not have test.method={}. failing to default methods.", klass.getName(), methodName); + } + } + return list; + } + + + @Override + protected List<FrameworkMethod> computeTestMethods() { + return computeTestMethodsForClass(getTestClass().getJavaClass(), super.computeTestMethods()); + } + + public static class LoggedInvokeMethod extends InvokeMethod { + private final FrameworkMethod method; + private final String name; + + public LoggedInvokeMethod(FrameworkMethod method, Object target) { + super(method, target); + this.method = method; + name = method.getName(); + } + + @Override + public void evaluate() throws Throwable { + LOG.info("RUNNING TEST METHOD {}", name); + try { + super.evaluate(); + Runtime rt = Runtime.getRuntime(); + long usedKB = (rt.totalMemory() - rt.freeMemory()) / 1024; + LOG.info("Memory used {}", usedKB); + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + while (tg.getParent() != null) { + tg = tg.getParent(); + } + LOG.info("Number of threads {}", tg.activeCount()); + } catch (Throwable t) { + // The test method threw an exception, but it might be an + // expected exception as defined in the @Test annotation. + // Check the annotation and log an appropriate message. + Test annotation = this.method.getAnnotation(Test.class); + if (annotation != null && annotation.expected() != null && + annotation.expected().isAssignableFrom(t.getClass())) { + LOG.info("TEST METHOD {} THREW EXPECTED EXCEPTION {}", name, + annotation.expected()); + } else { + LOG.info("TEST METHOD FAILED {}", name, t); + } + throw t; + } + LOG.info("FINISHED TEST METHOD {}", name); + } + } + + @Override + protected Statement methodInvoker(FrameworkMethod method, Object test) { + return new LoggedInvokeMethod(method, test); + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/JaasConfiguration.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/JaasConfiguration.java b/zookeeper-server/src/test/java/org/apache/zookeeper/JaasConfiguration.java new file mode 100644 index 0000000..bf145d0 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/JaasConfiguration.java @@ -0,0 +1,75 @@ +/** + * 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; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; + +/** + * This helper class allows to programmatically create a JAAS configuration. + * Each section must have a name and a login module, and a set of key/values + * to describe login options. + * + * Example: + * jaas = new JaasConfiguration(); + * jaas.addSection("Server", "org.apache.zookeeper.server.auth.DigestLoginModule", + * "username", "passowrd"); + */ +public class JaasConfiguration extends javax.security.auth.login.Configuration { + private final Map<String, AppConfigurationEntry[]> sections = + new HashMap<String, AppConfigurationEntry[]>(); + + public JaasConfiguration() { + } + + /** + * Add a section to the jaas.conf + * @param name Section name + * @param loginModuleName Login module name + * @param args login key/value args + */ + public void addSection(String name, String loginModuleName, String... args) { + Map<String, String> conf = new HashMap<String, String>(); + // loop through the args (must be key/value sequence) + for (int i = 0; i < args.length - 1; i += 2) { + conf.put(args[i], args[i + 1]); + } + addSection(name, loginModuleName, conf); + } + + /** + * Add a section to the jaas.conf + * @param name Section name + * @param loginModuleName Login module name + * @param conf login key/value args + */ + public void addSection(String name, String loginModuleName, final Map<String,String> conf) { + AppConfigurationEntry[] entries = new AppConfigurationEntry[1]; + entries[0] = new AppConfigurationEntry(loginModuleName, LoginModuleControlFlag.REQUIRED, conf); + this.sections.put(name, entries); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String appName) { + return sections.get(appName); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/MockPacket.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/MockPacket.java b/zookeeper-server/src/test/java/org/apache/zookeeper/MockPacket.java new file mode 100644 index 0000000..f4bb19a --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/MockPacket.java @@ -0,0 +1,46 @@ +/** + * 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; + +import org.apache.zookeeper.proto.RequestHeader; +import org.apache.zookeeper.proto.ReplyHeader; +import org.apache.jute.Record; +import org.apache.zookeeper.ZooKeeper.WatchRegistration; +import java.nio.ByteBuffer; + +public class MockPacket extends ClientCnxn.Packet { + + public MockPacket(RequestHeader requestHeader, ReplyHeader replyHeader, + Record request, Record response, + WatchRegistration watchRegistration) { + super(requestHeader, replyHeader, request, response, watchRegistration); + } + + public MockPacket(RequestHeader requestHeader, ReplyHeader replyHeader, + Record request, Record response, + WatchRegistration watchRegistration, boolean readOnly) { + super(requestHeader, replyHeader, request, response, watchRegistration, readOnly); + } + + public ByteBuffer createAndReturnBB() { + createBB(); + return this.bb; + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/MultiResponseTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/MultiResponseTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/MultiResponseTest.java new file mode 100644 index 0000000..75d9a12 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/MultiResponseTest.java @@ -0,0 +1,72 @@ +/* + * 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; + +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.ByteBufferInputStream; +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +public class MultiResponseTest extends ZKTestCase { + public void testRoundTrip() throws IOException { + MultiResponse response = new MultiResponse(); + + response.add(new OpResult.CheckResult()); + response.add(new OpResult.CreateResult("foo-bar")); + response.add(new OpResult.DeleteResult()); + + Stat s = new Stat(); + s.setCzxid(546); + response.add(new OpResult.SetDataResult(s)); + + MultiResponse decodedResponse = codeDecode(response); + + Assert.assertEquals(response, decodedResponse); + Assert.assertEquals(response.hashCode(), decodedResponse.hashCode()); + } + + @Test + public void testEmptyRoundTrip() throws IOException { + MultiResponse result = new MultiResponse(); + MultiResponse decodedResult = codeDecode(result); + + Assert.assertEquals(result, decodedResult); + Assert.assertEquals(result.hashCode(), decodedResult.hashCode()); + } + + private MultiResponse codeDecode(MultiResponse request) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); + request.serialize(boa, "result"); + baos.close(); + ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); + bb.rewind(); + + BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb)); + MultiResponse decodedRequest = new MultiResponse(); + decodedRequest.deserialize(bia, "result"); + return decodedRequest; + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/MultiTransactionRecordTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/MultiTransactionRecordTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/MultiTransactionRecordTest.java new file mode 100644 index 0000000..d33a3d7 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/MultiTransactionRecordTest.java @@ -0,0 +1,68 @@ +/** + * 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; + +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.server.ByteBufferInputStream; +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +public class MultiTransactionRecordTest extends ZKTestCase { + @Test + public void testRoundTrip() throws IOException { + MultiTransactionRecord request = new MultiTransactionRecord(); + request.add(Op.check("check", 1)); + request.add(Op.create("create", "create data".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, ZooDefs.Perms.ALL)); + request.add(Op.delete("delete", 17)); + request.add(Op.setData("setData", "set data".getBytes(), 19)); + + MultiTransactionRecord decodedRequest = codeDecode(request); + + Assert.assertEquals(request, decodedRequest); + Assert.assertEquals(request.hashCode(), decodedRequest.hashCode()); + } + + @Test + public void testEmptyRoundTrip() throws IOException { + MultiTransactionRecord request = new MultiTransactionRecord(); + MultiTransactionRecord decodedRequest = codeDecode(request); + + Assert.assertEquals(request, decodedRequest); + Assert.assertEquals(request.hashCode(), decodedRequest.hashCode()); + } + + private MultiTransactionRecord codeDecode(MultiTransactionRecord request) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); + request.serialize(boa, "request"); + baos.close(); + ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); + bb.rewind(); + + BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb)); + MultiTransactionRecord decodedRequest = new MultiTransactionRecord(); + decodedRequest.deserialize(bia, "request"); + return decodedRequest; + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignment.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignment.java b/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignment.java new file mode 100644 index 0000000..5c5b093 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignment.java @@ -0,0 +1,210 @@ +/** + * 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; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Assign ports to tests */ +public final class PortAssignment { + private static final Logger LOG = LoggerFactory.getLogger(PortAssignment.class); + + // The available port range that we use stays away from the ephemeral port + // range, which the OS will assign to client socket connections. We can't + // coordinate with the OS on the assignment of those ports, so it's best to + // stay out of that range to avoid conflicts. Typical ranges for ephemeral + // ports are: + // - IANA suggests 49152 - 65535 + // - Linux typically uses 32768 - 61000 + // - FreeBSD modern versions typically use the IANA suggested range + // - Windows modern versions typically use the IANA suggested range + private static final int GLOBAL_BASE_PORT = 11221; + private static final int GLOBAL_MAX_PORT = 32767; + + private static PortRange portRange = null; + private static int nextPort; + + /** + * Assign a new, unique port to the test. This method works by assigning + * ports from a valid port range as identified by the total number of + * concurrent test processes and the ID of this test process. Each + * concurrent test process uses an isolated range, so it's not possible for + * multiple test processes to collide on the same port. Within the port + * range, ports are assigned in monotonic increasing order, wrapping around + * to the beginning of the range if needed. As an extra precaution, the + * method attempts to bind to the port and immediately close it before + * returning it to the caller. If the port cannot be bound, then it tries + * the next one in the range. This provides some resiliency in case the port + * is otherwise occupied, such as a developer running other servers on the + * machine running the tests. + * + * @return port + */ + public synchronized static int unique() { + if (portRange == null) { + portRange = setupPortRange(System.getProperty("test.junit.threads"), + System.getProperty("sun.java.command")); + nextPort = portRange.getMinimum(); + } + int candidatePort = nextPort; + for (;;) { + ++candidatePort; + if (candidatePort > portRange.getMaximum()) { + candidatePort = portRange.getMinimum(); + } + if (candidatePort == nextPort) { + throw new IllegalStateException(String.format( + "Could not assign port from range %s. The entire " + + "range has been exhausted.", portRange)); + } + try { + ServerSocket s = new ServerSocket(candidatePort); + s.close(); + nextPort = candidatePort; + LOG.info("Assigned port {} from range {}.", nextPort, portRange); + return nextPort; + } catch (IOException e) { + LOG.debug("Could not bind to port {} from range {}. " + + "Attempting next port.", candidatePort, portRange, e); + } + } + } + + /** + * Sets up the port range to be used. In typical usage, Ant invokes JUnit, + * possibly using multiple JUnit processes to execute multiple test suites + * concurrently. The count of JUnit processes is passed from Ant as a system + * property named "test.junit.threads". Ant's JUnit runner receives the + * thread ID as a command line argument of the form threadid=N, where N is an + * integer in the range [1, ${test.junit.threads}]. It's not otherwise + * accessible, so we need to parse it from the command line. This method + * uses these 2 pieces of information to split the available ports into + * disjoint ranges. Each JUnit process only assigns ports from its own range + * in order to prevent bind errors during concurrent test runs. If any of + * this information is unavailable or unparseable, then the default behavior + * is for this process to use the entire available port range. This is + * expected when running tests outside of Ant. + * + * @param strProcessCount string representation of integer process count, + * typically taken from system property test.junit.threads + * @param cmdLine command line containing threadid=N argument, typically + * taken from system property sun.java.command + * @return port range to use + */ + static PortRange setupPortRange(String strProcessCount, String cmdLine) { + Integer processCount = null; + if (strProcessCount != null && !strProcessCount.isEmpty()) { + try { + processCount = Integer.valueOf(strProcessCount); + } catch (NumberFormatException e) { + LOG.warn("Error parsing test.junit.threads = {}.", + strProcessCount, e); + } + } + + Integer threadId = null; + if (processCount != null) { + if (cmdLine != null && !cmdLine.isEmpty()) { + Matcher m = Pattern.compile("threadid=(\\d+)").matcher(cmdLine); + if (m.find()) { + try { + threadId = Integer.valueOf(m.group(1)); + } catch (NumberFormatException e) { + LOG.warn("Error parsing threadid from {}.", cmdLine, e); + } + } + } + } + + final PortRange newPortRange; + if (processCount != null && processCount > 1 && threadId != null) { + // We know the total JUnit process count and this test process's ID. + // Use these values to calculate the valid range for port assignments + // within this test process. We lose a few possible ports to the + // remainder, but that's acceptable. + int portRangeSize = (GLOBAL_MAX_PORT - GLOBAL_BASE_PORT) / + processCount; + int minPort = GLOBAL_BASE_PORT + ((threadId - 1) * portRangeSize); + int maxPort = minPort + portRangeSize - 1; + newPortRange = new PortRange(minPort, maxPort); + LOG.info("Test process {}/{} using ports from {}.", threadId, + processCount, newPortRange); + } else { + // If running outside the context of Ant or Ant is using a single + // test process, then use all valid ports. + newPortRange = new PortRange(GLOBAL_BASE_PORT, GLOBAL_MAX_PORT); + LOG.info("Single test process using ports from {}.", newPortRange); + } + + return newPortRange; + } + + /** + * Contains the minimum and maximum (both inclusive) in a range of ports. + */ + static final class PortRange { + private final int minimum; + private final int maximum; + + /** + * Creates a new PortRange. + * + * @param minimum lower bound port number + * @param maximum upper bound port number + */ + PortRange(int minimum, int maximum) { + this.minimum = minimum; + this.maximum = maximum; + } + + /** + * Returns maximum port in the range. + * + * @return maximum + */ + int getMaximum() { + return maximum; + } + + /** + * Returns minimum port in the range. + * + * @return minimum + */ + int getMinimum() { + return minimum; + } + + @Override + public String toString() { + return String.format("%d - %d", minimum, maximum); + } + } + + /** + * There is no reason to instantiate this class. + */ + private PortAssignment() { + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignmentTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignmentTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignmentTest.java new file mode 100644 index 0000000..28691c6 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignmentTest.java @@ -0,0 +1,80 @@ +/** + * 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; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collection; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.Test; + +@RunWith(Parameterized.class) +@Parameterized.UseParametersRunnerFactory(ZKParameterized.RunnerFactory.class) +public class PortAssignmentTest { + + private final String strProcessCount; + private final String cmdLine; + private final int expectedMinimumPort; + private final int expectedMaximumPort; + + @Parameters + public static Collection<Object[]> data() { + return Arrays.<Object[]>asList( + new Object[] { "8", "threadid=1", 11221, 13913 }, + new Object[] { "8", "threadid=2", 13914, 16606 }, + new Object[] { "8", "threadid=3", 16607, 19299 }, + new Object[] { "8", "threadid=4", 19300, 21992 }, + new Object[] { "8", "threadid=5", 21993, 24685 }, + new Object[] { "8", "threadid=6", 24686, 27378 }, + new Object[] { "8", "threadid=7", 27379, 30071 }, + new Object[] { "8", "threadid=8", 30072, 32764 }, + new Object[] { "1", "threadid=1", 11221, 32767 }, + new Object[] { "2", "threadid=1", 11221, 21993 }, + new Object[] { "2", "threadid=2", 21994, 32766 }, + new Object[] { null, null, 11221, 32767 }, + new Object[] { "", "", 11221, 32767 }); + } + + public PortAssignmentTest(String strProcessCount, String cmdLine, + int expectedMinimumPort, int expectedMaximumPort) { + this.strProcessCount = strProcessCount; + this.cmdLine = cmdLine; + this.expectedMinimumPort = expectedMinimumPort; + this.expectedMaximumPort = expectedMaximumPort; + } + + @Test + public void testSetupPortRange() { + PortAssignment.PortRange portRange = PortAssignment.setupPortRange( + strProcessCount, cmdLine); + assertEquals(buildAssertionMessage("minimum"), expectedMinimumPort, + portRange.getMinimum()); + assertEquals(buildAssertionMessage("maximum"), expectedMaximumPort, + portRange.getMaximum()); + } + + private String buildAssertionMessage(String checkType) { + return String.format("strProcessCount = %s, cmdLine = %s, checking %s", + strProcessCount, cmdLine, checkType); + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/43d71c2e/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesCmdTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesCmdTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesCmdTest.java new file mode 100644 index 0000000..c239404 --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesCmdTest.java @@ -0,0 +1,345 @@ +/** + * 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; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.zookeeper.Watcher.Event.EventType; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.test.ClientBase; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Testing remove watches using command line + */ +public class RemoveWatchesCmdTest extends ClientBase { + private static final Logger LOG = LoggerFactory + .getLogger(RemoveWatchesCmdTest.class); + private ZooKeeper zk; + private ZooKeeperMain zkMain; + + @Override + public void setUp() throws Exception { + super.setUp(); + zk = createClient(); + zkMain = new ZooKeeperMain(zk); + } + + @Override + public void tearDown() throws Exception { + if (zk != null) { + zk.close(); + } + super.tearDown(); + } + + /** + * Test verifies default options. When there is no passed options, + * removewatches command will use default options - WatcherType.ANY and + * local=false + */ + @Test(timeout = 30000) + public void testRemoveWatchesWithNoPassedOptions() throws Exception { + List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>(); + expectedEvents.add(EventType.ChildWatchRemoved); + expectedEvents.add(EventType.DataWatchRemoved); + MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 2); + + zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.create("/testnode2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + LOG.info("Adding childwatcher to /testnode1 and /testnode2"); + zk.getChildren("/testnode1", myWatcher); + zk.getChildren("/testnode2", myWatcher); + + LOG.info("Adding datawatcher to /testnode1 and /testnode2"); + zk.getData("/testnode1", myWatcher, null); + zk.getData("/testnode2", myWatcher, null); + + String cmdstring = "removewatches /testnode1"; + LOG.info("Remove watchers using shell command : {}", cmdstring); + zkMain.cl.parseCommand(cmdstring); + Assert.assertTrue("Removewatches cmd fails to remove child watches", + zkMain.processZKCmd(zkMain.cl)); + LOG.info("Waiting for the DataWatchRemoved event"); + myWatcher.matches(); + + // verifying that other path child watches are not affected + Assert.assertTrue( + "Failed to find child watches for the path testnode2", zk + .getChildWatches().contains("/testnode2")); + Assert.assertTrue("Failed to find data watches for the path testnode2", + zk.getDataWatches().contains("/testnode2")); + } + + /** + * Test verifies deletion of NodeDataChanged watches + */ + @Test(timeout = 30000) + public void testRemoveNodeDataChangedWatches() throws Exception { + LOG.info("Adding data watcher using getData()"); + List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>(); + expectedEvents.add(EventType.DataWatchRemoved); + MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1); + + zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.getData("/testnode1", myWatcher, null); + + String cmdstring = "removewatches /testnode1 -d"; + LOG.info("Remove watchers using shell command : {}", cmdstring); + zkMain.cl.parseCommand(cmdstring); + Assert.assertTrue("Removewatches cmd fails to remove data watches", + zkMain.processZKCmd(zkMain.cl)); + + LOG.info("Waiting for the DataWatchRemoved event"); + myWatcher.matches(); + + // verifying that other path data watches are removed + Assert.assertEquals( + "Data watches are not removed : " + zk.getDataWatches(), 0, zk + .getDataWatches().size()); + } + + /** + * Test verifies deletion of NodeCreated data watches + */ + @Test(timeout = 30000) + public void testRemoveNodeCreatedWatches() throws Exception { + List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>(); + expectedEvents.add(EventType.DataWatchRemoved); + MyWatcher myWatcher1 = new MyWatcher("/testnode1", expectedEvents, 1); + MyWatcher myWatcher2 = new MyWatcher("/testnode1/testnode2", + expectedEvents, 1); + // Adding pre-created watcher + LOG.info("Adding NodeCreated watcher"); + zk.exists("/testnode1", myWatcher1); + zk.exists("/testnode1/testnode2", myWatcher2); + + String cmdstring1 = "removewatches /testnode1 -d"; + LOG.info("Remove watchers using shell command : {}", cmdstring1); + zkMain.cl.parseCommand(cmdstring1); + Assert.assertTrue( + "Removewatches cmd fails to remove pre-create watches", + zkMain.processZKCmd(zkMain.cl)); + myWatcher1.matches(); + Assert.assertEquals( + "Failed to remove pre-create watches :" + zk.getExistWatches(), + 1, zk.getExistWatches().size()); + Assert.assertTrue( + "Failed to remove pre-create watches :" + zk.getExistWatches(), + zk.getExistWatches().contains("/testnode1/testnode2")); + + String cmdstring2 = "removewatches /testnode1/testnode2 -d"; + LOG.info("Remove watchers using shell command : {}", cmdstring2); + zkMain.cl.parseCommand(cmdstring2); + Assert.assertTrue("Removewatches cmd fails to remove data watches", + zkMain.processZKCmd(zkMain.cl)); + + myWatcher2.matches(); + Assert.assertEquals( + "Failed to remove pre-create watches : " + zk.getExistWatches(), + 0, zk.getExistWatches().size()); + } + + /** + * Test verifies deletion of NodeChildrenChanged watches + */ + @Test(timeout = 30000) + public void testRemoveNodeChildrenChangedWatches() throws Exception { + List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>(); + expectedEvents.add(EventType.ChildWatchRemoved); + MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1); + + zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + LOG.info("Adding child changed watcher"); + zk.getChildren("/testnode1", myWatcher); + + String cmdstring = "removewatches /testnode1 -c"; + LOG.info("Remove watchers using shell command : {}", cmdstring); + zkMain.cl.parseCommand(cmdstring); + Assert.assertTrue("Removewatches cmd fails to remove child watches", + zkMain.processZKCmd(zkMain.cl)); + myWatcher.matches(); + Assert.assertEquals( + "Failed to remove child watches : " + zk.getChildWatches(), 0, + zk.getChildWatches().size()); + } + + /** + * Test verifies deletion of NodeDeleted watches + */ + @Test(timeout = 30000) + public void testRemoveNodeDeletedWatches() throws Exception { + LOG.info("Adding NodeDeleted watcher"); + List<EventType> expectedEvents = new ArrayList<Watcher.Event.EventType>(); + expectedEvents.add(EventType.ChildWatchRemoved); + expectedEvents.add(EventType.NodeDeleted); + MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1); + + zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.create("/testnode1/testnode2", "data".getBytes(), + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk.getChildren("/testnode1/testnode2", myWatcher); + zk.getChildren("/testnode1", myWatcher); + + String cmdstring = "removewatches /testnode1 -c"; + LOG.info("Remove watchers using shell command : {}", cmdstring); + zkMain.cl.parseCommand(cmdstring); + Assert.assertTrue("Removewatches cmd fails to remove child watches", + zkMain.processZKCmd(zkMain.cl)); + LOG.info("Waiting for the ChildWatchRemoved event"); + myWatcher.matches(); + Assert.assertEquals( + "Failed to remove child watches : " + zk.getChildWatches(), 1, + zk.getChildWatches().size()); + + Assert.assertTrue( + "Failed to remove child watches :" + zk.getChildWatches(), zk + .getChildWatches().contains("/testnode1/testnode2")); + + // verify node delete watcher + zk.delete("/testnode1/testnode2", -1); + myWatcher.matches(); + } + + /** + * Test verifies deletion of any watches + */ + @Test(timeout = 30000) + public void testRemoveAnyWatches() throws Exception { + verifyRemoveAnyWatches(false); + } + + /** + * Test verifies deletion of watches locally when there is no server + * connection + */ + @Test(timeout = 30000) + public void testRemoveWatchesLocallyWhenNoServerConnection() + throws Exception { + verifyRemoveAnyWatches(true); + } + + private void verifyRemoveAnyWatches(boolean local) throws Exception { + final Map<String, List<EventType>> pathVsEvent = new HashMap<String, List<EventType>>(); + LOG.info("Adding NodeChildrenChanged, NodeDataChanged watchers"); + final CountDownLatch watcherLatch = new CountDownLatch(2); + Watcher watcher = new Watcher() { + + @Override + public void process(WatchedEvent event) { + switch (event.getType()) { + case ChildWatchRemoved: + case DataWatchRemoved: { + addWatchNotifications(pathVsEvent, event); + watcherLatch.countDown(); + break; + } + case NodeChildrenChanged: + case NodeDataChanged: { + addWatchNotifications(pathVsEvent, event); + break; + } + } + } + + private void addWatchNotifications( + final Map<String, List<EventType>> pathVsEvent, + WatchedEvent event) { + List<EventType> events = pathVsEvent.get(event.getPath()); + if (null == events) { + events = new ArrayList<Watcher.Event.EventType>(); + pathVsEvent.put(event.getPath(), events); + } + events.add(event.getType()); + } + }; + zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.getChildren("/testnode1", watcher); + zk.getData("/testnode1", watcher, null); + String cmdstring = "removewatches /testnode1 -a"; + if (local) { + LOG.info("Stopping ZK server to verify deletion of watches locally"); + stopServer(); + cmdstring = "removewatches /testnode1 -a -l"; + } + + LOG.info("Remove watchers using shell command : {}", cmdstring); + zkMain.cl.parseCommand(cmdstring); + Assert.assertTrue( + "Removewatches cmd fails to remove child/data watches", + zkMain.processZKCmd(zkMain.cl)); + LOG.info("Waiting for the WatchRemoved events"); + watcherLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals("Didn't receives WatchRemoved events!", 1, + pathVsEvent.size()); + Assert.assertTrue( + "Didn't receives DataWatchRemoved!", + pathVsEvent.get("/testnode1").contains( + EventType.DataWatchRemoved)); + Assert.assertTrue("Didn't receives ChildWatchRemoved!", pathVsEvent + .get("/testnode1").contains(EventType.ChildWatchRemoved)); + } + + private class MyWatcher implements Watcher { + private final String path; + private String eventPath; + private final CountDownLatch latch; + private final List<EventType> expectedEvents = new ArrayList<EventType>(); + + public MyWatcher(String path, List<EventType> expectedEvents, int count) { + this.path = path; + this.latch = new CountDownLatch(count); + this.expectedEvents.addAll(expectedEvents); + } + + public void process(WatchedEvent event) { + LOG.debug("Event path : {}, eventPath : {}" + + new Object[] { path, event.getPath() }); + this.eventPath = event.getPath(); + if (expectedEvents.contains(event.getType())) { + latch.countDown(); + } + } + + public boolean matches() throws InterruptedException { + if (!latch.await(CONNECTION_TIMEOUT / 3, TimeUnit.MILLISECONDS)) { + LOG.error("Failed to get watch notifications!"); + return false; + } + LOG.debug("Client path : {} eventPath : {}", new Object[] { path, + eventPath }); + return path.equals(eventPath); + } + } + +}